Home > Net >  React complains that hook is not used inside of a body function when my component is passed into ano
React complains that hook is not used inside of a body function when my component is passed into ano

Time:12-15

I have a component that uses another component as a renderer. However, React doesn't allow me to use hooks in the renderer component as it throws the following error:

Error: Invalid hook call. Hooks can only be called inside of the body of a function component.

Here's an example of what I have in my display component which is an error boundary:

class DisplayComponent extends Component {
  // ...

  render() {
    const { rendererComponent } = this.props;
    return rendererComponent({ enabled: true, name: 'hello-world' })
  }
}

As you can see, the rendererComponent prop in the DisplayComponent is called as a function though it's a component and I pass in some props into it as I call it this way.

Now, in my other file where I use the DisplayComponent, it looks something like this:

const RendererComponent = (props) => {
  const { enabled, name } = props;

  const [testState, setTestState] = useState(); // Error: Invalid hook call

  useEffect(() => {
    // Error: Invalid hook call
  }, [testState]);

  return (
    <div>{name} - {enabled}</div>
  )
}

const App = () => {
  return (
    <DisplayComponent rendererComponent={RendererComponent} />
  )
}

Here's the key thing. For some reason, I can't use hooks such as useEffect, useState, etc in the RendererComponent. If I use it, React will throw the error I mentioned above that the hook is invalid. But I would like to have some logic which requires hooks in the RendererComponent.

How can I use hooks to have states and effects in a component that is called the way I did?

CodePudding user response:

You are making a function call instead of mounting the component, those are two different calls and syntax, change it to:

class DisplayComponent extends Component {
  render() {
    // Custom component must be Capitalized
    const { rendererComponent: RenderedComponent } = this.props;
    return <RenderedComponent enabled name="hello-world" />;
  }
}

// Notice that the JSX is transpiled to
React.createElement(RenderedComponent, {
  enabled: true,
  name: "hello-world"
});

In React, you can only mount a component by calling its API with React.createElement (JSX is its sugar syntax), while a simple function call is purely JS which does not trigger React API i.e won't register the component to the component tree. Therefore inner implementation like hooks has no meaning, you will lose state on every render (the Reconciliation won't be triggered).

  • Related