React useState传入相同引用仍触发组件重渲染问题
问题核心原因
你碰到的现象完全是React的正常运行表现,useState本身没有识别异常,本质是两个认知误区:
- 对
useState跳过更新的规则记混了:只要调用setState传入的值和当前状态用Object.is比较相等,React就会直接跳过后续重渲染和effect执行,这个规则不是只在首次更新才生效 - 错把正常的状态更新重渲染,当成了「传相同值触发的无效重渲染」
对应代码的完整执行流程
顺着你的代码走一遍运行时序,就能完全对上控制台打印的所有日志:
const arr = [ { name: 'Khaled', id: 0 }, { name: 'Mohamed', id: 1 }, { name: 'Ahmed', id: 2 }, { name: 'Mohsen', id: 3 }, ]; function App() { console.log('num $1'); const [same, setSame] = useState({}); useEffect(() => { console.log('num $3'); const test = arr.find((el) => el.id === 0); console.log(Object.is(same, test)); setSame(test); }, [same]); console.log('num $2'); return <div>welcome</div>; }
- 首次挂载阶段
组件第一次跑函数体,先打印num $1,初始化same状态为你传入的空对象{},接着打印num $2,完成首次DOM渲染把<div>welcome</div>挂到页面上。
挂载完成后执行绑定的useEffect,打印num $3。这时候拿到的test是arr里id为0的那个对象的引用,和当前存的空对象状态做Object.is对比结果是false,控制台打出false,随后调用setSame(test)触发状态更新,React安排下一次重渲染。 - 第一次重渲染阶段
组件第二次跑函数体,打印num $1。React对比新传入的test和上一轮的空对象,两个值引用不一样,正常把same更新为test,走完整个渲染流程,接着打印num $2提交DOM更新。
更新提交完检查effect依赖,发现same的值从空对象变成了arr[0]的引用,依赖变了,所以再次执行effect回调,打印num $3。这时候再取test还是arr[0]的同一个引用,和当前的same做Object.is对比结果是true,控制台打出true。
这时候再调用setSame(test),React检测到传入的值和当前状态完全相等,直接终止更新流程,不会再触发重渲染,也不会重复跑effect,日志到这里就停了。
关键点说明
你看到的两次num $1/num $2打印,一次是组件首次挂载的必然执行,一次是same从空对象改成arr[0]引用的正常更新渲染,根本不是传相同值触发的无效重渲染。最后一次传相同引用调用setSame的时候,React已经正确跳过了更新,没有产生多余渲染。
控制台实际运行日志:
内容的提问来源于stack exchange,提问作者Muhammad Hossam
相关产品推荐
相关产品推荐

