Home > database >  Rendered more hooks than during the previous render when using hooks in subcomponent
Rendered more hooks than during the previous render when using hooks in subcomponent

Time:02-02

I am trying to understand how I can use React Hooks in subcomponents without triggering an error.

Let's say I have a super simple component (all the code is runnable here - https://codesandbox.io/embed/xenodochial-wright-mwqlk?fontsize=14&hidenavigation=1&theme=dark):

import { useEffect, useState } from "react";

const TestComponent = (index) => {
  const [a, setA] = useState("nothing");

  useEffect(() => {
    setA(index);
  }, [setA, index]);

  return <>{a}</>;
};

export default TestComponent;

And it is used like this:

import { useEffect, useState } from "react";
import Component from "./Component";

export default function App() {
  const [arr, setArr] = useState([]);

  useEffect(() => {
    setArr([1]);
  }, []);

  const component = arr.map((_, index) => Component(index));

  return <div>{component}</div>;
}

Now I am getting an error:

Error
Rendered more hooks than during the previous render.
▶ 5 stack frames were collapsed.
TestComponent
/src/Component.js:4:29
  1 | import { useEffect, useState } from "react";
  2 | 
  3 | const TestComponent = (index) => {
> 4 |   const [a, setA] = useState("nothing");
    |                             ^
  5 | 
  6 |   useEffect(() => {
  7 |     setA(index);

There are more details in the console:

Warning: React has detected a change in the order of Hooks called by App. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks

   Previous render            Next render
   ------------------------------------------------------
1. useState                   useState
2. useEffect                  useEffect
3. undefined                  useState
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    at App (https://mwqlk.csb.app/src/App.js:24:41)

I understand the basic concept of the hooks rules that I cannot use hooks after if statements or such, they should always be on the top level.

However, this case is clearly not using any conditionals for declaring the hooks. All additional hooks are actually in a subcomponent. So why are they accounted for by React as part of the App component?

So, how can I safely use hooks in imported subcomponents then?

CodePudding user response:

You're calling your react component like a function instead of using it as a component. This causes some weirdness with rendering. If you change your .map line to

const component = arr.map((_, index) => <Component index={index} />);

and destructure your props appropriately in Component

const TestComponent = ({ index }) => {

everything works as expected.

CodePudding user response:

Here's how you should pass

App.js:

import { useEffect, useState } from "react";
import Component from "./Component";

export default function App() {
  const [arr, setArr] = useState([]);

  useEffect(() => {
    setArr([10,12,12,12,12]);
  }, []);

  return <>{arr.map((_, index) => (
    <Component key={index} index={index}/>
  ))}</>
}

Test component:

import { useEffect, useState } from "react";

const TestComponent = ({index}) => {
  const [a, setA] = useState("");

  useEffect(() => {
    setA(index);
  }, []);

  return <>{a}</>;
};

export default TestComponent;

CodePudding user response:

You should refactor in this way

Component.js

import { useEffect, useState } from "react";

const TestComponent = ({index}) => {
  const [a, setA] = useState("nothing");

  useEffect(() => {
    setA(index);
  }, [setA, index]);

  return <>{a}</>;
};

export default TestComponent;

App.js

import { useEffect, useState } from "react";
import TestComponent from "./Component";

export default function App() {
  const [arr, setArr] = useState([]);

  useEffect(() => {
    setArr([1]);
  }, []);

  const component = arr.map((_, index) => <TestComponent index={index}/>);

  return <div>{component}</div>;
}
  •  Tags:  
  • Related