You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js 13中onClick箭头函数调用setState触发无限循环问题

Next.js 13中setState触发无限循环的解决办法

你的问题核心根本不是onClick里调用setState的写法错了——你贴的简化版组件代码完全没问题,点击按钮只会正常切换状态,不会触发无限循环。真正的问题出在原组件里获取多语言数据的方式。

问题根源

你在组件里用了const locales = use(getLocales()),每次组件渲染时,getLocales()这个异步函数都会被重新执行一遍。当你点击按钮调用setDropdownCollapsed修改状态时,组件会触发重渲染,这又会再次调用getLocales()发起Apollo查询,查询完成后数据更新又会触发重渲染,如此往复就形成了无限循环。

正确的解决方式

在Next.js 13的客户端组件里,应该用Apollo Client专门为Suspense设计的useSuspenseQuery钩子来获取数据,而不是手动调用client.query再配合React的use函数。

修改后的完整组件代码如下:

'use client'

import { useState } from "react";
import { useSuspenseQuery } 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}) {
    // 用useSuspenseQuery替代手动调用client.query和use函数
    const { data } = useSuspenseQuery(localesQuery);
    const locales = data.i18NLocales.data;

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

    return (
        <button className={className} onClick={() => setDropdownCollapsed(!dropdownCollapsed)}>
            <LanguageIcon className="h-5 w-5 text-dark dark:text-white"/>
            {/* 根据dropdownCollapsed状态控制下拉框显示/隐藏 */}
            {dropdownCollapsed && (
                <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) => (
                            <li key={locale.id}> {/* 用locale的唯一id作为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>
    )
}

额外优化点

  1. 原代码里的下拉框没有根据dropdownCollapsed状态控制显示,现在加上了{dropdownCollapsed && (...)},点击按钮就能正常切换下拉框的显示/隐藏。
  2. 列表项的key改用locale.id这种唯一标识,避免用index可能导致的列表更新异常。

内容的提问来源于stack exchange,提问作者Douwe Pausma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 20:42:11