Search code examples
reactjstypescriptreact-hooksreact-typescript

React hook error when changing size of rendered array


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>
    ));
}

Solution

  • 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.