如何优化自定义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
相关产品推荐
相关产品推荐

