Home > Enterprise >  React hook error when changing size of rendered array
React hook error when changing size of rendered array

Time:09-21

I get the following error: React Error: "Rendered more hooks than during the previous render", and it is because inside a mapped array that I render are buttons that have their own useState hooks.

So I have an array of projects that I render from a list. Initially, only 3 projects are shown, and clicking a button will load the whole list.

The problem is that inside project can be multiple ProjectButtons, and those ProjectButtons are components because I want to use special hover states using the useState hook.

But when I change the size of the project list being rendered, it throws an error because of the useState hook inside the ProjectButton component.

import { projects } from "../lib/projectList";

const Projects: FC = () => {
    // Initially use a portion of the array
    const [projectArray, setProjectArray] = useState(projects.slice(0, 3));

    // Load the whole array on button click
    const loadMoreProjects = () => {
        setProjectArray([...projects]);
    }

    const ProjectButton = (button: { type: string, link: string }) => {
        // Removing this useState hook fixes the problem, but I need it for my design
        const [hoverColor, setHoverColor] = useState("#0327d8");

        const handleMouseEnter = () => {
            setHoverColor("white");
        }
        const handleMouseLeave = () => {
            setHoverColor(original);
        }

        return (
            <a href={button.link} rel="noreferrer" target="_blank" key={button.link}>
                <button onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
                    <WebsiteIcon className="projectButtonIcon" fill={hoverColor} />
                    <p>{button.type}</p>
                </button>
            </a>
        );
    }

    return projectArray.map(project => (
        ...
        <div className="projectLinks">
            {project.buttons.map(button => ProjectButton(button))}
        </div>
        ...
        <Button onClick={loadMoreProjects}>Load More</Button>
    ));
}

CodePudding user response:

You've defined ProjectButton within your Projects component, so you're breaking the rule of hooks - specifically "Only Call Hooks at the Top Level".

Move the ProjectButton component out of the scope of Projects and it will be happy.

CodePudding user response:

This is happening because you are using hooks inside a function and it should be used directly inside a component. This can solved if you create ProjectButton as a component instead of function.

Here is the updated code:

import { projects } from "../lib/projectList";



const ProjectButton = (button) => {
  // Removing this useState hook fixes the problem, but I need it for my design
  const [hoverColor, setHoverColor] = useState("#0327d8");

  const handleMouseEnter = () => {
    setHoverColor("white");
  };
  const handleMouseLeave = () => {
    setHoverColor(original);
  };

  return (
    <a href={button.link} rel="noreferrer" target="_blank" key={button.link}>
      <button onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
        <WebsiteIcon className="projectButtonIcon" fill={hoverColor} />
        <p>{button.type}</p>
      </button>
    </a>
  );
};

const Projects: FC = () => {
    // Initially use a portion of the array
    const [projectArray, setProjectArray] = useState(projects.slice(0, 3));

    // Load the whole array on button click
    const loadMoreProjects = () => {
        setProjectArray([...projects]);
    }

    return projectArray.map(project => (
        ...
        <div className="projectLinks">
            {project.buttons.map((button) => (
              <ProjectButton {...button} />
             ))}
        </div>
        ...
        <Button onClick={loadMoreProjects}>Load More</Button>
    ));
}
  • Related