使用Zustand时出现无限重渲染问题求助
问题:Zustand状态更新导致组件无限重渲染
之前使用Redux+Redux-Saga配合类组件时,在componentDidMount中派发API请求不会出现重复调用问题。改用Zustand+函数组件后,状态能正常更新,但组件会持续重渲染直至超出最大更新深度,需要解决在状态方法内更新状态时避免不必要重渲染的问题。
现有代码
HomeContainer组件
import { useEffect } from "react"; import { appStore } from "../App/store"; export default function HomeContainer(props: any): any { const getCarouselData = appStore((state: any) => state.getCarousels); const carousels = appStore((state: any) => state.carousels); useEffect(() => { if (carousels.length === 0) { getCarouselData(); } }, [carousels, getCarouselData]); console.log("carousels", carousels); return <p>Home Container</p>; }
Loading Slice
const loadingSlice = (set: any, get: any) => ({ loading: false, setLoading: (isLoading: boolean) => { set((state: any) => ({ ...state, loading: isLoading })); }, }); export default loadingSlice;
App Store
import create from "zustand"; import homeSlice from "../Home/store"; import loadingSlice from "../Layout/state"; export const appStore = create((set: any, get: any) => ({ ...loadingSlice(set, get), ...homeSlice(set, get), }));
Home Slice(问题代码)
import axios from "axios"; import { appStore } from "../App/store"; const homeSlice = (set: any, get: any) => ({ carousels: [], getCarousels: () => { appStore.getState().setLoading(true); axios .get("api-endpoint") .then((res) => { set((state: any) => ({ ...state, carousels: res.data, })); }) .catch((err) => { console.log(err); }); appStore.getState().setLoading(true); }, }); export default homeSlice;
问题原因
- 方法引用不稳定:每次组件重渲染时,
appStore((state) => state.getCarousels)会返回新的函数引用,导致useEffect依赖项变化,触发重复执行。 - 逻辑冗余+未处理loading状态:
getCarousels中重复调用setLoading(true),且请求完成后未将loading重置为false;同时useEffect的依赖循环加上状态更新,形成无限重渲染的闭环。
解决方案
1. 稳定方法引用
使用Zustand的shallow比较,确保获取的方法引用不会随组件重渲染变化:
import { useEffect } from "react"; import { appStore } from "../App/store"; import { shallow } from "zustand/shallow"; export default function HomeContainer(props: any): any { const { getCarouselData, carousels } = appStore( (state) => ({ getCarouselData: state.getCarousels, carousels: state.carousels, }), shallow ); useEffect(() => { if (carousels.length === 0) { getCarouselData(); } }, [carousels, getCarouselData]); console.log("carousels", carousels); return <p>Home Container</p>; }
2. 修正Home Slice逻辑
- 避免通过
appStore.getState()调用其他slice方法,直接用Zustand提供的get()方法获取当前状态中的方法 - 修复loading状态的设置逻辑,请求完成后重置loading:
import axios from "axios"; const homeSlice = (set: any, get: any) => ({ carousels: [], getCarousels: async () => { // 直接通过get()获取setLoading方法 get().setLoading(true); try { const res = await axios.get("api-endpoint"); set((state: any) => ({ ...state, carousels: res.data, })); } catch (err) { console.log(err); } finally { // 请求无论成功/失败都关闭loading get().setLoading(false); } }, }); export default homeSlice;
3. 优化useEffect执行时机
如果只需要在组件挂载时请求一次数据,直接使用空依赖数组:
useEffect(() => { if (carousels.length === 0) { getCarouselData(); } }, []); // 仅组件挂载时执行一次
内容的提问来源于stack exchange,提问作者M Ansyori
相关产品推荐
相关产品推荐

