为何用带参数函数定义Jotai atom会导致组件无限重渲染?
问题:使用Jotai实现语言状态时组件无限重渲染
我正在用Jotai和React实现网站的语言状态功能,功能能正常工作,但组件开始无限重渲染,浏览器控制台报出最大更新深度超限的警告。
原子定义文件
import { atom } from "jotai"; export const languageState = atom('tr') export const marineHeaderObject = (langState) => atom({ buttonDataArray: [ { label: langState === 'tr' ? 'Anasayfa' : 'Home ', path: 'home', isDropdown: false }, { label: langState === 'tr' ? 'Kurumsal' : 'Corporate ' , path: '', isDropdown: false}, { label: langState === 'tr' ? 'Modellerimiz' : 'Models ', path: '', isDropdown: true}, { label: langState === 'tr' ? 'Tekne Kiralama' : 'Boat Rental ', path: '', isDropdown: false}, { label: langState === 'tr' ? 'Güverte Kaplama' : 'Deck Covering ', path: '', isDropdown: false}, { label: langState === 'tr' ? 'İletişim' : 'Contact ', path: 'contact', isDropdown: false}, ], logoPath: '' } )
组件使用代码
import { useEffect, useState } from 'react' import { useAtom } from 'jotai' import Header from '../../components/Header'; import { marineHeaderObject } from '../../states'; import { languageState } from '../../states'; const myPage = () => { const [langState] = useAtom(languageState) const [marineHeader] = useAtom(marineHeaderObject(langState)) return ( <div className="pageWrapper"> <Header headerObject={marineHeader} /> </div> ); } export default myPage;
Header组件代码
import NavButton from './NavButton' const Header = ({ headerObject }) => { return ( <div className="header"> <div className="logo"> <a> <img height="70" width="184" src="../assets/images/logov2.svg" /> </a> </div> <div className="navigationBar"> <nav> { headerObject.buttonDataArray.map((button, index) => { return ( <NavButton key={index} buttonData={button} /> ) }) } </nav> </div> </div> ); } export default Header;
浏览器控制台报错
270react_devtools_backend.js:4026 Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render. at Maycotex (http://localhost:3000/main.9f8185fe9033d1ee3856.hot-update.js:36:69) at Routes (http://localhost:3000/static/js/bundle.js:41590:5) at Router (http://localhost:3000/static/js/bundle.js:41523:15) at BrowserRouter (http://localhost:3000/static/js/bundle.js:40332:5) at App
问题原因
核心问题出在marineHeaderObject的定义和使用方式:
- 每次
myPage组件渲染时,调用marineHeaderObject(langState)都会创建一个全新的Jotai原子实例。 - Jotai的
useAtom钩子会监听这个原子的变化,由于每次渲染都是新原子,Jotai会判定依赖发生了变化,触发组件重新渲染。 - 重新渲染时又会创建新的原子,形成"创建新原子→触发重渲染→再创建新原子"的无限循环,最终导致最大更新深度超限。
解决方案
把marineHeaderObject改成依赖languageState的派生原子,让它内部直接读取languageState的值,而不是通过参数接收,这样就能保证整个应用中只有这一个原子实例,避免重复创建。
修复后的原子定义文件
import { atom } from "jotai"; export const languageState = atom('tr') // 改成派生原子,内部读取languageState export const marineHeaderObject = atom((get) => { const langState = get(languageState); return { buttonDataArray: [ { label: langState === 'tr' ? 'Anasayfa' : 'Home ', path: 'home', isDropdown: false }, { label: langState === 'tr' ? 'Kurumsal' : 'Corporate ' , path: '', isDropdown: false}, { label: langState === 'tr' ? 'Modellerimiz' : 'Models ', path: '', isDropdown: true}, { label: langState === 'tr' ? 'Tekne Kiralama' : 'Boat Rental ', path: '', isDropdown: false}, { label: langState === 'tr' ? 'Güverte Kaplama' : 'Deck Covering ', path: '', isDropdown: false}, { label: langState === 'tr' ? 'İletişim' : 'Contact ', path: 'contact', isDropdown: false}, ], logoPath: '' } })
修复后的组件使用代码
import { useAtom } from 'jotai' import Header from '../../components/Header'; import { marineHeaderObject } from '../../states'; import { languageState } from '../../states'; const myPage = () => { // 不再需要手动传递langState,派生原子会自动追踪依赖 const [langState] = useAtom(languageState) const [marineHeader] = useAtom(marineHeaderObject) return ( <div className="pageWrapper"> <Header headerObject={marineHeader} /> </div> ); } export default myPage;
原理说明
- 派生原子通过
get函数读取其他原子的值,Jotai会自动建立依赖关系,只有当languageState的值真正变化时,marineHeaderObject才会重新计算并触发组件更新。 - 整个应用中
marineHeaderObject只会存在一个实例,不会在每次组件渲染时重新创建,彻底打破无限循环的触发条件。
内容的提问来源于stack exchange,提问作者eaidy
相关产品推荐
相关产品推荐

