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

使用Zustand的shallow仍触发组件重渲染问题(Next.js13+TS)

解决Zustand组件不必要重渲染的问题

问题原因

你的组件重渲染的核心原因有三个:

  1. 选择器返回新数组导致引用变化:每次store更新时,选择器(store) => [store.mode, store.setMode, store.timeMode, store.setTimeMode]都会生成新的数组对象。如果用默认的引用比较(或错误的自定义比较函数直接比较数组引用),Zustand会判定返回值变化,触发组件重渲染。
  2. 未正确导入shallow比较器:若你没有从zustand/shallow导入shallow,传入的shallow变量实际是undefined,Zustand会忽略该参数,继续使用默认的引用比较逻辑。
  3. action函数无需放入选择器返回值:Zustand的action函数引用是稳定的(创建store时仅定义一次),将它们放入选择器返回的数组/对象中完全没必要,反而会增加比较复杂度。

解决方案

步骤1:正确导入shallow比较器

首先确保从Zustand的shallow模块导入:

import { shallow } from 'zustand/shallow';

步骤2:拆分状态与action的获取

将状态值和action函数分开获取,仅对状态值使用shallow比较,action直接单独获取:

function MyComponent(){
  // 仅选择需要监听的状态值,用shallow做浅比较
  const { mode, timeMode } = useModeStore(
    (store) => ({ 
      mode: store.mode, 
      timeMode: store.timeMode 
    }),
    shallow
  );

  // 单独获取action函数,它们的引用是稳定的,不会触发重渲染
  const setMode = useModeStore((store) => store.setMode);
  const setTimeMode = useModeStore((store) => store.setTimeMode);

  const renderCountRef = useRef(0);

  useEffect(() => {
    renderCountRef.current++;
    console.log('Component re-rendered', renderCountRef.current);
  });
  console.log("rendered");

  return <SOMETHING />;
}

步骤3:修复Store中的错误逻辑

你的incrementMode函数直接修改了原state,违反了Zustand的不可变状态原则,可能导致意外问题,修复为:

incrementMode: () => set((state) => ({ testMode: state.testMode + 1 }))

可选:自定义比较函数的正确写法

如果你坚持使用数组返回值,自定义比较函数需要逐个比较数组元素,而不是直接比较数组引用:

(prev, next) => {
  if (prev.length !== next.length) return false;
  for (let i = 0; i < prev.length; i++) {
    if (prev[i] !== next[i]) return false;
  }
  console.log("true , component skipped");
  return true;
}

内容的提问来源于stack exchange,提问作者Mohammed Mehdi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 18:50:33