在 HoC 中使用反应钩子时的警告

Posted

技术标签:

【中文标题】在 HoC 中使用反应钩子时的警告【英文标题】:Warning when using react hooks in HoC 【发布时间】:2019-10-10 19:24:53 【问题描述】:

我创建了一个高阶组件,它应该为我的组件添加一些额外的功能。但是,当我在这个组件中使用 react 钩子时,我会收到以下 eslint 警告。

无法在回调中调用 React Hook "React.useEffect"。反应 Hooks 必须在 React 函数组件或自定义 React 中调用 挂钩功能。 (react-hooks/rules-of-hooks)

为什么我会收到此警告?在 HoC 中使用钩子是否被认为是不好的做法?

小例子:

const Hello = props => <p>Greetings props.name</p>;

const Wrapper = Component => props => 
  React.useEffect(() => 
    // Do something here
  , []);
  return <Component ...props />;
;

export default Wrapper(Hello)

代码沙盒: https://codesandbox.io/s/proud-tree-5kscc

【问题讨论】:

hook 解决的复杂性之一是通过custom hooks 在组件之间共享逻辑的能力。将您的 HOC 重构为自定义钩子 @AsafAviv 自定义挂钩并不能解决所有问题。 HOC 组件中最强大的功能之一是能够提前返回并渲染不同的组件,现在试试钩子吧。 @AsafAviv 这不是render props 模式的用途吗? @PrashanD 可以,但现在您可以将状态提取到自定义钩子中并在组件中使用该钩子而不是渲染道具 【参考方案1】:

转换

props => 
  React.useEffect(() => 
    // Do something here
  , []);
  return <Component ...props />;
;

在你的 HOC 内部到一个函数(react-hooks/rules-of-hooks 会抛出你在 HOC 返回的箭头函数中使用时显示的警告)

所以,改成

const Wrapper = Component =>
  function Comp(props) 
    React.useEffect(() => 
      console.log("useEffect");
    , []);
    return <Component ...props />;
  ;

效果被触发。

Here is a working example on codesandbox

【讨论】:

我总是以这种方式在 HOC 返回的功能组件中使用钩子。 返回的函数不能是匿名函数。所以()=&gt;function ()是不允许的,function myComponent()是可以的。 为什么const Wrapper = Component =&gt; props =&gt; 不正确?我一直在做很多事情来将钩子转换为 HOC。我一直都做错了吗。这仅适用于 linting 规则吗?【参考方案2】:

official React Hooks documentation 说:

不要从常规 javascript 函数中调用 Hooks。相反,您可以:

✅ 从 React 函数组件调用 Hooks。

✅ 从自定义 Hooks 调用 Hooks。

正如@AsafAviv 所说,您应该将您的 HOC 重构为自定义挂钩,以避免违反 Rules of Hooks。


顺便说一下原因是described in the FAQ:

React 如何将 Hook 调用与组件关联起来?

React 跟踪当前呈现的组件。由于 Hooks 的规则,我们知道 Hooks 只能从 React 组件调用(或自定义 Hooks,也只能从 React 组件调用)。

每个组件都有一个内部“记忆单元”列表。它们只是 JavaScript 对象,我们可以在其中放置一些数据。当你调用 useState() 之类的 Hook 时,它会读取当前单元格(或在第一次渲染期间对其进行初始化),然后将指针移动到下一个单元格。这就是多个 useState() 调用各自获取独立本地状态的方式。

【讨论】:

我知道 Hooks 的规则确保仅从 React 组件内部调用反应钩子。但是这个函数确实返回了一个 React 组件,所以我没有看到这个问题。 linting 规则是不是太宽泛了? 在这种情况下,您还可以添加解决方案吗? 这一定是规则中的错误..当内部函数命名时它不会抱怨,这应该没有任何区别。 显然,错误是有效的。 github.com/facebook/create-react-app/issues/7033我只是不同意。【参考方案3】:

在您定义了 HoC 的文件中,只需将以下内容添加到文件顶部:

/* eslint-disable react-hooks/rules-of-hooks */

Hooks 和 higher-order components 是两个完全不同的东西。任何说 HoC 可以被 hook 替换的人要么从未真正写过 HoC,要么从未玩过语义游戏。

当我编写 HoC 时,我经常不得不禁用 rules-of-hooks eslint 规则,因为该规则对于它认为是钩子或组件来说过于严格。 HoC 更像是一个组件而不是一个钩子,但规则不承认这一点。

【讨论】:

【参考方案4】:

您可以在功能组件或自定义 Hooks 中使用 react hooks。 重写你的 HOC:

const Hello = props => <p>Greetings props.name</p>;

const HookDoSomething = () => 
  React.useEffect(() => 
    // Do something here
  , []);


const Wrapper = Component => props => 
  HookDoSomething()
  return <Component ...props />;
;

export default Wrapper(Hello)

【讨论】:

【参考方案5】:

Short Answer: 你只需要把回调改成 PascalCase 函数或者 useSomething 函数。这是因为 eslint 规则有一些启发式规则,您需要遵循它才能检测组件。

您需要将代码更改为

const Wrapper = Component => 
  return function WithWrapper(props)
    React.useEffect(() => 
        // Do something here
      , []);
    return <Component ...props />;
  

【讨论】:

以上是关于在 HoC 中使用反应钩子时的警告的主要内容,如果未能解决你的问题,请参考以下文章

反应 useEffect 警告以放置缺少的依赖项。但是钩子中的依赖值发生了变化

当我的 useEffect 钩子在 react-apollo 突变后被触发时,如何从反应中解决这个警告?

组件不会重新渲染,但 redux 状态已通过反应钩子更改

如何在useEffect钩子反应中停止内存泄漏

使用钩子获取数据时的意外行为

使用上下文和钩子更新未安装组件的状态 - 反应原生