使用Zustand的shallow仍触发组件重渲染问题(Next.js13+TS)
解决Zustand组件不必要重渲染的问题
问题原因
你的组件重渲染的核心原因有三个:
- 选择器返回新数组导致引用变化:每次store更新时,选择器
(store) => [store.mode, store.setMode, store.timeMode, store.setTimeMode]都会生成新的数组对象。如果用默认的引用比较(或错误的自定义比较函数直接比较数组引用),Zustand会判定返回值变化,触发组件重渲染。 - 未正确导入
shallow比较器:若你没有从zustand/shallow导入shallow,传入的shallow变量实际是undefined,Zustand会忽略该参数,继续使用默认的引用比较逻辑。 - 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
相关产品推荐
相关产品推荐

