React未消费Context的函数返回对象出现Context值问题咨询
核心问题根源
你的reducer中Object.assign的参数顺序写反了,这是所有异常的诱因。Object.assign(target, ...sources)的第一个参数是会被直接修改的目标对象,后续参数是用于合并的源对象。你写的return Object.assign(action.payload, state)相当于把原有state的所有属性直接覆盖到Test返回的payload对象上,并且把修改后的payload作为新state返回,直接篡改了Test函数返回的rtn对象。
问题1:第一次打印的对象为什么包含Context的f1、reset函数?
- 调用
f1(value)时,value就是Test返回的rtn对象,这个对象会作为action.payload传入reducer - 写反参数的
Object.assign会把当前state里的resetKey、f1、reset、初始obj1/obj2全部写到rtn对象上,直接修改了原对象 - 浏览器控制台对引用类型的打印是懒求值的:打印
rtn的时候是Test执行的时刻,此时rtn还是正常的{obj1, obj2},但等你点击展开控制台的对象时,reducer已经执行完、rtn已经被修改,所以你看到的展开结果里有f1、reset这些Context相关属性。
问题2:为什么第一次日志里rtn的obj1和单独打印的obj1值不一致?
- 单独打印的
obj1是Test函数内部创建的独立对象,全程没有被修改,所以打印结果一直是你定义的{name: 'obj1', value: [1,2,3]} rtn对象的obj1属性在Object.assign执行时,被state里的初始obj1(也就是{name: 'obj2', value: []})覆盖了,所以展开rtn看到的是被覆盖后的结果,和单独打印的原始obj1自然不一致。- 你可以用
console.log(JSON.parse(JSON.stringify(rtn)), obj1, obj2)打印对象快照,就能看到打印时刻的rtn确实符合预期,展开后的异常是后续修改和控制台懒求值共同导致的。
问题3:去掉useEffect后第一次输出消失,以及为什么Test没消费Context但返回值有Context内容?
- 两次打印的来源:第一次是Caller组件初始渲染时执行
Test()触发的打印,第二次是f1触发dispatch导致state更新、ContextProvider重渲染,Caller跟着重渲染,再次执行Test()触发的第二次打印。 - 去掉useEffect之后,就不会执行
f1(value),也就不会触发dispatch和state更新,Caller只会渲染一次,自然只有一次打印;同时因为没有执行写反参数的Object.assign,rtn对象不会被修改,你就看不到带Context属性的异常打印了,并不是第一次输出消失了,是它本来就是正常的结果,和预期一致而已。 - Test确实没有消费Context,Context的属性出现在rtn里完全是reducer逻辑导致的:你把Test返回的对象作为了
Object.assign的目标参数,把state的所有属性都写进了这个对象,和Context消费没有任何关系。
修复方案
把reducer里f1分支的返回值改成创建新对象的写法,不要修改原有payload:
case "f1": return Object.assign({}, state, action.payload); // 或者用对象展开写法 // return { ...state, ...action.payload }
修改后所有异常都会消失。
内容的提问来源于stack exchange,提问作者KS_HTK
相关产品推荐
相关产品推荐

