React中ES6 Map状态变量修改为何呈现同步特性?
问题
在React中通过useState初始化了一个ES6 Map类型的状态变量errorMap,编写handleErrorMapChange方法通过setErrorMap传入新Map实例来修改状态,但调用该方法后errorMap会立即更新,这和其他状态setter的异步特性不符,想知道这是偶然还是必然原因。
相关代码
初始化代码
const [errorMap, setErrorMap] = useState(new Map<string, string>([ [ERROR_KEY_RECEIPT_NUMBER_STEP_1, ''], [ERROR_KEY_FORM_NUMBER_STEP_1, ''], ... ]));
修改方法
const handleErrorMapChange = (id: string, value: any) => { setErrorMap(new Map<string, any>(errorMap.set(id, value))); }
调用示例
handleErrorMapChange(ERROR_KEY_FORM_NUMBER_STEP_1, 'String'); console.log(JSON.stringify(Object.fromEntries(errorMap))); // 立即获取最新内容
原因分析
这是必然结果,核心问题出在违反了React状态不可变的原则:
Map.set()是可变操作,它会直接修改调用它的原Map实例,并且返回修改后的原Map。你先执行errorMap.set(id, value)时,已经同步修改了当前的errorMap状态变量,之后再用这个被篡改的原Map创建新实例,原状态早已经被改变,所以console.log能立刻看到最新值。- React的
setErrorMap本身还是异步的,但你跳过了它的状态隔离机制,直接修改原状态对象,才导致了这种不符合预期的同步更新现象。
正确写法
要保证状态不可变,必须基于原状态创建副本,修改副本后再传入setter:
const handleErrorMapChange = (id: string, value: any) => { // 复制原Map得到新实例 const newErrorMap = new Map(errorMap); // 修改副本 newErrorMap.set(id, value); // 更新状态 setErrorMap(newErrorMap); }
也可以用更简洁的链式写法,注意要先复制再执行set操作:
const handleErrorMapChange = (id: string, value: any) => { setErrorMap(new Map(errorMap).set(id, value)); }
这样修改后,原状态errorMap不会被同步修改,只有当React完成状态更新并触发重新渲染后,才能拿到新的状态值,符合useState的异步特性。
内容的提问来源于stack exchange,提问作者gene b.
相关产品推荐
相关产品推荐

