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

使用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;

问题原因

  1. 方法引用不稳定:每次组件重渲染时,appStore((state) => state.getCarousels)会返回新的函数引用,导致useEffect依赖项变化,触发重复执行。
  2. 逻辑冗余+未处理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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 09:45:35