const count = useStore(s => s.count);
const list = useStore(s => s.list);
// 或者
const { count, list } = useStore(useShallow(s => ({ count: s.count, list: s.list })));

计数从 3 加到 4,React Profiler 里这次提交耗时 480ms,列表 86 个条目全部 render,列表数据一个字都没变。先别急着动参数,把报错原文和时间点记下来,顺序反了容易白折腾。

原因不复杂,选择器写成 state => ({ list: state.list, count: state.count }),每次返回新对象,默认的 Object.is 比较永远不相等,store 一变更所有订阅组件都收到通知。父级重渲染再把 86 个子项带着走,外面包了 memo 也拦不住。

围绕「Zustand 选择器重渲染」排查时,命令行跑一遍比在面板里点十次有用,面板会替你做掉不少隐式操作。动手前把原文件复制一份带时间戳,出问题立刻换回去,比现场分析快得多。

一次只改一处,改完立刻验证,几个改动叠在一起,出问题就说不清是谁导致的。

处理上按这个顺序:选择器只取用到的字段 不要返回新对象;需要多个字段就分开订阅 或者用 useShallow 做浅比较;子项用 memo 包住 确认传入的 props 引用稳定;改完在 Profiler 里看 commit 数 应该从 86 降到 2。

改完只确认一件事:计数变化时 Profiler 里只有计数组件重新 render,列表项的 commit 数为 0。

作者 9823189