【问题标题】:HoC with React Hooks带有 React Hooks 的 HoC
【发布时间】:2020-01-06 23:25:38
【问题描述】:

我正在尝试使用Context APIclass component 移植到react hooks,但我无法弄清楚收到错误的具体原因。

首先,我的代码:

// contexts/sample.jsx
import React, { createContext, useState, useContext } from 'react'
const SampleCtx = createContext()

const SampleProvider = (props) => {
  const [ value, setValue ] = useState('Default Value')
  const sampleContext = { value, setValue }
  return (
    <SampleCtx.Provider value={sampleContext}>
      {props.children}
    </SampleCtx.Provider>
  )
}

const useSample = (WrappedComponent) => {
  const sampleCtx = useContext(SampleCtx)
  return (
    <SampleProvider>
      <WrappedComponent
        value={sampleCtx.value}
        setValue={sampleCtx.setValue} />
    </SampleProvider>
  )
}

export {
  useSample
}

// Sends.jsx
import React, { Component, useState, useEffect } from 'react'
import { useSample } from '../contexts/sample.jsx'

const Sends = (props) => {
  const [input, setInput ] = useState('')

  const handleChange = (e) => {
    setInput(e.target.value)
  }
  const handleSubmit = (e) => {
    e.preventDefault()

    props.setValue(input)
  }

  useEffect(() => {
    setInput(props.value)
  }, props.value)

  return (
    <form onSubmit={handleSubmit}>
      <input value={input} onChange={handleChange} />
      <button type="submit">Submit</button>
    </form>
  )
}

我得到的错误:

Invariant Violation:无效的钩子调用。钩子只能在函数组件的主体内部调用。这可能是由于以下原因之一: 1. 你可能有不匹配的 React 版本和渲染器(例如 React DOM) 2. 你可能违反了 Hooks 规则 3. 你可能有多个 React 副本相同的应用程序 请参阅https://reactjs.org/warnings/invalid-hook-call-warning.html 以获取有关如何调试和修复此问题的提示。

我的代码说明:

我使用Context API 来管理状态,之前我使用class components 来制作视图。我希望结构简单明了,不需要更多细节。

我认为它应该也可以工作,&lt;Sends /&gt; 组件被传递到 useSample HoC 函数中,并被 sample.jsx&lt;SampleProvider&gt; 组件包裹,因此 &lt;Sends /&gt; 可以使用 propsSampleCtx 上下文提供。但结果是失败。

HoC 模式与React hooks 一起使用是否无效?还是通过props将突变函数(即useState()制作的setValue)交给其他组件无效?或者,使用hooks 将2 个或更多function components 放在一个文件中是否无效?请指正具体是什么原因。

【问题讨论】:

  • @JosephD。我修正了错字,这是一个微不足道的错字。我没有使用&lt;Consumer /&gt;,因为我想改用useContext。即使我使用了useContext 钩子,我是否必须使用&lt;Consumer /&gt;
  • 不。 useContext()&lt;Consumer /&gt; 是等价的。

标签: javascript reactjs react-hooks react-context


【解决方案1】:

所以 HOC 和 Context 是不同的 React 概念。因此,让我们将其分为两部分。

提供者

提供者的主要职责是提供上下文值。上下文值通过useContext() 使用

const SampleCtx = createContext({});

export const SampleProvider = props => {
  const [value, setValue] = useState("Default Value");
  const sampleContext = { value, setValue };

  useEffect(() => console.log("Context Value: ", value)); // only log when value changes

  return (
    <SampleCtx.Provider value={sampleContext}>
      {props.children}
    </SampleCtx.Provider>
  );
};

HOC

消费者。使用 useContext() 钩子并添加额外的道具。返回一个新组件。

const withSample = WrappedComponent => props => { // curry
  const sampleCtx = useContext(SampleCtx);
  return (
    <WrappedComponent
      {...props}
      value={sampleCtx.value}
      setValue={sampleCtx.setValue}
    />
  );
};

然后使用 HOC:

export default withSample(Send)

组成提供者和消费者 (HOC),我们有:

import { SampleProvider } from "./provider";
import SampleHOCWithHooks from "./send";

import "./styles.css";

function App() {
  return (
    <div className="App">
      <SampleProvider>
        <SampleHOCWithHooks />
      </SampleProvider>
    </div>
  );
}

完整代码见Code Sandbox

【讨论】:

  • eslint 会对 HOC 产生警告,因为在回调方法中不能调用钩子。 React Hook "useContext" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function.eslint(react-hooks/rules-of-hooks)
【解决方案2】:

高阶组件是接受一个组件并返回另一个组件的函数,返回的组件可以是类组件、带有钩子的功能组件或者它可以没有状态逻辑。 在您的示例中,您从 useSample 返回 jsx。

const useSample = (WrappedComponent) => {
  const sampleCtx = useContext(SampleCtx)
  return ( // <-- here
    <SampleProvider>
      <WrappedComponent
        value={sampleCtx.value}
        setValue={sampleCtx.setValue} />
    </SampleProvider>
  )
}

如果你想制作一个 HOC,你可以这样做

const withSample = (WrappedComponent) => {
  return props => {
        const sampleCtx = useContext(SampleCtx)
        <WrappedComponent
            value={sampleCtx.value}
            setValue={sampleCtx.setValue} {...props} />
    }
}

【讨论】:

  • 我试过了,但在这种情况下,context 无法正确生成。 sampleCtxundefined,这意味着useContext 不会正确生成context
  • 另外,我们不是还要使用&lt;SampleProvider /&gt;吗?
  • provider 应该包装你想要访问上下文状态的应用程序的整个部分,你不需要将每个组件包装在一个 provider 中。例如,如果您希望这些数据在您的整个应用程序中可用,则使用提供程序包装您的根组件。
猜你喜欢
  • 2020-11-05
  • 1970-01-01
  • 1970-01-01
  • 2019-09-30
  • 2019-10-23
  • 2020-05-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多