如何为React的useSyncExternalStore配置自定义相等比较器?
解决useSyncExternalStore数组引用导致的不必要重渲染问题
useSyncExternalStore本身不支持直接传入自定义比较函数,但可以通过包装快照函数的方式,实现自定义内容比较的效果,避免JSON序列化带来的性能损耗。
核心思路是:缓存上一次的快照引用,在getSnapshot阶段先对比当前快照和缓存快照的内容,如果内容一致,就返回缓存的旧引用,让Object.is判断为相等,从而跳过重渲染;如果内容不同,再更新缓存并返回新快照。
实现代码示例
针对数组的场景,我们可以封装一个自定义Hook,搭配浅比较(或深比较)函数来处理:
import { useSyncExternalStore, useRef } from 'react'; // 数组浅比较函数:适用于元素为基本类型的场景 function shallowEqualArrays(a, b) { if (a === b) return true; if (a.length !== b.length) return false; for (let i = 0; i < a.length; i++) { if (a[i] !== b[i]) return false; } return true; } // 包装useSyncExternalStore,添加自定义比较逻辑 function useCustomExternalStore(subscribe, getSnapshot) { const snapshotRef = useRef(); const customGetSnapshot = () => { const currentSnapshot = getSnapshot(); // 针对数组做自定义内容比较 if (Array.isArray(currentSnapshot)) { if (shallowEqualArrays(currentSnapshot, snapshotRef.current)) { return snapshotRef.current; // 返回旧引用,跳过重渲染 } } else { // 非数组类型保留默认Object.is判断 if (Object.is(currentSnapshot, snapshotRef.current)) { return snapshotRef.current; } } // 内容不同时更新缓存并返回新快照 snapshotRef.current = currentSnapshot; return currentSnapshot; }; return useSyncExternalStore(subscribe, customGetSnapshot); }
使用说明
- 如果数组元素是引用类型(比如对象),可以把
shallowEqualArrays替换为深比较函数,但要注意优化深比较的逻辑,只对比必要的字段,避免不必要的性能开销。 - 这个方案的性能远优于JSON序列化,因为直接做数组元素的逐次对比,没有序列化/反序列化的额外消耗。
- 缓存的快照用
useRef存储,确保跨渲染周期保留引用,不会被React的渲染机制重置。
内容的提问来源于stack exchange,提问作者siim
相关产品推荐
相关产品推荐

