Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Warning when using react hooks in HoC

I've created a a higher order component that is supposed to add some additional functionality to my components. However, when I use react hooks in this component, I get the following eslint warning.

React Hook "React.useEffect" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function. (react-hooks/rules-of-hooks)

Why am I getting this warning? Is it considered bad practice to use hooks in a HoC?

Minimal example:

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

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

export default Wrapper(Hello)

codesandbox: https://codesandbox.io/s/proud-tree-5kscc

like image 768
Stephan Olsen Avatar asked May 24 '19 08:05

Stephan Olsen


Video Answer


3 Answers

Convert the

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

inside your HOC to a function (react-hooks/rules-of-hooks is throwing that warning you showed when used in an arrow function returned by a HOC)

So, change it to

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

and the effect gets triggered.

Here is a working example on codesandbox

like image 158
s14k51 Avatar answered Oct 12 '22 12:10

s14k51


The official React Hooks documentation says:

Don’t call Hooks from regular JavaScript functions. Instead, you can:

✅ Call Hooks from React function components.

✅ Call Hooks from custom Hooks.

As @AsafAviv said, you should refactor your HOC into a custom hook to avoid violation the Rules of Hooks.


The reason is described in the FAQ by the way:

How does React associate Hook calls with components?

React keeps track of the currently rendering component. Thanks to the Rules of Hooks, we know that Hooks are only called from React components (or custom Hooks — which are also only called from React components).

There is an internal list of “memory cells” associated with each component. They’re just JavaScript objects where we can put some data. When you call a Hook like useState(), it reads the current cell (or initializes it during the first render), and then moves the pointer to the next one. This is how multiple useState() calls each get independent local state.

like image 3
Igor Soloydenko Avatar answered Oct 12 '22 13:10

Igor Soloydenko


Inside the file where you have your HoC defined, simply add the following to the top of the file:

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

Hooks and higher-order components are two completely different things. Anyone who says a HoC can be replaced by a hook has either never actually written a HoC or playing semantics games.

When I write a HoC, I often have to disable the rules-of-hooks eslint rule because the rule is too stringent wrt what it thinks is a hook or component. HoC is more akin to a component than a hook, but the rule does not recognize this.

like image 1
smac89 Avatar answered Oct 12 '22 13:10

smac89