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

Zustand存储值未变更但组件仍重复渲染问题求助

问题根源与解决方案

你的组件出现不必要重渲染的核心原因是:每次调用updateSettings时,无论数据是否真的变化,都会生成新的嵌套对象或顶层settings对象引用。Zustand默认用**严格相等(===)**对比selector返回值,只要引用变化,就会触发组件重渲染——哪怕对象内容完全一致。

比如原实现中:

  • 即使value和state.settings[key]内容完全一致,{...state.settings[key], ...value}也会生成新的对象引用
  • 修改side_bars时,{...state.settings, [key]: ...}会生成新的settings对象,但settings.app的引用其实未变。如果你的代码存在多次调用updateSettings(比如遍历currentSettings批量更新)或初始填充时重复设置settings,就会触发不必要的更新。

以下是更合理的解决方案,无需依赖性能较差的JSON.stringify:


解决方案1:手动浅对比控制更新

通过浅对比判断内容是否真的变化,仅在有差异时返回新状态,避免无意义的引用更新:

import { create } from 'zustand';

// 自定义浅对比函数,无需额外依赖
function shallowEqual(objA, objB) {
  if (objA === objB) return true;
  if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
    return false;
  }
  const keysA = Object.keys(objA);
  if (keysA.length !== Object.keys(objB).length) return false;
  for (const key of keysA) {
    if (!Object.prototype.hasOwnProperty.call(objB, key) || objA[key] !== objB[key]) {
      return false;
    }
  }
  return true;
}

const useTestStore = create((set) => ({
  settings: {},
  updateSettings: (key, value) => {
    set((state) => {
      const currentValue = state.settings[key];
      // 仅当内容真正变化时,才生成新状态
      if (currentValue && shallowEqual(currentValue, value)) {
        return state; // 返回原状态,不触发任何更新
      }
      return {
        settings: {
          ...state.settings,
          [key]: {
            ...currentValue,
            ...value,
          },
        },
      };
    });
  },
}));

解决方案2:结合Immer简化嵌套更新

Zustand支持与Immer配合,更简洁地处理嵌套状态,同时自动复用未修改部分的对象引用:

import { create } from 'zustand';
import { produce } from 'immer';

const useTestStore = create((set) => ({
  settings: {},
  updateSettings: (key, value) => {
    set(produce((state) => {
      const currentValue = state.settings[key];
      if (currentValue && shallowEqual(currentValue, value)) {
        return; // 无变化则终止更新
      }
      // Immer会自动处理对象复用,仅修改目标部分
      state.settings[key] = { ...currentValue, ...value };
    }));
  },
}));

关于shallow的正确用法

你尝试的shallow适用于selector返回多个独立切片的场景,比如同时订阅app和side_bars时,用浅对比判断整体是否变化:

import { shallow } from 'zustand/shallow';

// 订阅多个切片时,用shallow做浅对比避免不必要重渲染
const { app, side_bars } = useTestStore((state) => ({
  app: state.settings.app,
  side_bars: state.settings.side_bars,
}), shallow);

对于单个嵌套对象的订阅,shallow和默认的===效果一致,因此之前使用时没有明显作用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 06:28:11