Search code examples
reactjsnext.jsreact-hooksonclickarrow-functions

Infinite loop when using setState in an arrow function in an onClick - Next 13


When I try to set a state by using an onClick I get an infinite loop.

My component

'use client'

import { use, useState } from "react";
import { gql } from "@apollo/client";
import client from "/apollo-client";
import { localesQuery } from '/graphql/queries';

// Import Components
import { LanguageIcon } from '@heroicons/react/24/solid'
import Image from 'next/image';

export default function LanguageSwitcher({className}) {
    const locales = use(getLocales())

    const [dropdownCollapsed, setDropdownCollapsed] = useState(false);

    return (
        <button className={className} onClick={() => setDropdownCollapsed(!dropdownCollapsed)}>
            <LanguageIcon className="h-5 w-5 text-dark dark:text-white"/>
            <div className="dropdown-list absolute right-0 -bottom-4 transform translate-y-full transition-colors duration-150 border border-gray-200 dark:border-gray-800 bg-white dark:bg-black md:bg-opacity-70 rounded-2xl overflow-hidden ">
                <ul className="w-40 flex flex-col">
                    {locales.map((locale, index) => (
                        <li key={index}>
                            <a className="flex items-center px-4 py-3 hover:bg-gray-100 dark:hover:bg-zinc-900 w-full">
                                <Image height="20" width="24" className="rounded mr-1.5" src={`images/${locale.attributes.code}.svg`} alt={`${locale.attributes.name} flag`} />
                                <span>{locale.attributes.name}</span>
                            </a>
                        </li>
                    ))}
                </ul>
            </div>
        </button>
    )
}

async function getLocales() {
    const { data } = await client.query({
      query: localesQuery,
    });

    return data.i18NLocales.data;
}

I tried adding an arrow function in between, but this only fixes it until I click the button, because then it renders again and starts the recursion.

Simplified version of my component:

import { useState } from "react";

export default function Component() {
    const [variable, setVariable] = useState(false);

    return (
        <button onClick={() => setVariable(!variable)}>Click me!</button>
    )
}

So my question is how do I setVariable with an onClick. I'd assume this was the right approach but maybe I am doing something wrong.


Solution

  • The way I fixed is to separate the fetching of data in to a child component. After reading this I learned that one should prevent fetching data and using hooks in the same component.