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

如何优化自定义useSelector Hook 避免组件不必要重渲染

优化自定义useSelector Hook避免不必要组件重渲染

问题根源

当前的useSelector直接返回选择器函数的执行结果,且未对结果做缓存;同时Context Provider的value每次state更新都会生成新的{state}对象,导致所有依赖该Context的组件都会触发重渲染——哪怕组件只用到了state中无关变化的属性。

解决方案

通过缓存选择器结果并对比前后值的变化,让组件仅在关注的状态真正改变时才重渲染。

1. 实现浅比较工具函数

用于判断两次选择结果是否“内容相等”,避免因对象引用变化但内容未变导致的重渲染:

function shallowEqual(a: any, b: any): boolean {
  if (Object.is(a, b)) return true;
  
  if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) {
    return false;
  }
  
  const keysA = Object.keys(a);
  const keysB = Object.keys(b);
  
  if (keysA.length !== keysB.length) return false;
  
  for (const key of keysA) {
    if (!Object.prototype.hasOwnProperty.call(b, key) || !Object.is(a[key], b[key])) {
      return false;
    }
  }
  
  return true;
}

2. 优化useSelector Hook

结合useRef缓存历史状态和结果,useMemo控制结果更新时机:

import { useContext, useMemo, useRef } from 'react';

const useSelector = <T>(selector: (state: State) => T) => {
  const { state } = useContext(StoreContext);
  
  const previousStateRef = useRef<State | undefined>();
  const previousResultRef = useRef<T | undefined>();
  
  const result = useMemo(() => {
    // 状态未变化,直接返回缓存结果
    if (previousStateRef.current === state) {
      return previousResultRef.current as T;
    }
    
    const newResult = selector(state);
    
    // 结果未发生浅变化,返回缓存结果
    if (previousResultRef.current !== undefined && shallowEqual(newResult, previousResultRef.current)) {
      return previousResultRef.current;
    }
    
    // 更新缓存
    previousStateRef.current = state;
    previousResultRef.current = newResult;
    
    return newResult;
  }, [state, selector]);
  
  return result;
};

3. 额外优化:缓存Context Provider的value

避免因state更新生成新的{state}对象触发Context订阅者的无意义更新:

function GlobalComponent(){
 const initialState = {
   reports: {depositReport: []},
   users: [],
 }

 const [state, dispatch] = useReducer(reducer, initialState);
 
 // 缓存Context值,仅在state变化时更新
 const contextValue = useMemo(() => ({ state }), [state]);

 return <StoreContext.Provider value={contextValue}></StoreContext.Provider>;
}

使用效果

优化后,组件仅在选择器返回的目标状态(如state.reports)真正变化时才会重渲染:

function Component() {
 const { depositReport } = useSelector(state => state.reports);
 // 修改state.users时,组件不会重渲染
}

扩展说明

如果需要处理嵌套更深的状态(需深对比),可将shallowEqual替换为深比较函数(如lodash的isEqual)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 17:55:57