React中setState修改Map未触发重渲染?两种写法差异解惑
React useState两种Map状态更新方式的差异解析
问题背景
在React函数组件中定义了Map类型的状态projMap:
const [projMap, setProjMap] = useState<Map<number, FolderModel>>(new Map<number, FolderModel>());
第一种更新写法无法触发UI重渲染:
React.useEffect(() => { if (folderProjList && folderProjList.length > 0) { if (currFolder) { let cachedExpand = projMap.get(currFolder.id)?.expand; let folderModel: FolderModel = { expand: cachedExpand ? cachedExpand : true, projects: folderProjList }; projMap.set(currFolder.id, folderModel); setProjMap(projMap); } } }, [folderProjList]);
改用函数式更新后,UI能正常触发重渲染:
React.useEffect(() => { if (folderProjList && folderProjList.length > 0) { if (currFolder) { setProjMap((prevMapState) => { const newMapState = new Map<number, FolderModel>(prevMapState); let cachedExpand = newMapState.get(currFolder.id)?.expand; let folderModel: FolderModel = { expand: cachedExpand ? cachedExpand : true, projects: folderProjList }; newMapState.set(currFolder.id, folderModel); return newMapState; }); } } }, [folderProjList]);
两种写法的核心差异
1. 第一种写法的问题
- 违反React状态不可变原则:直接修改了原状态
projMap(projMap.set(...)),Map是引用类型,修改操作会直接改变原状态对象的内容,但引用地址没有变化。 - 浅比较未触发重渲染:React判断是否需要重渲染的逻辑是浅比较前后状态的引用。当调用
setProjMap(projMap)时,传入的是同一个引用的对象,React认为状态没有发生变化,因此不会触发UI重渲染。
2. 第二种写法的正确逻辑
- 遵循状态不可变原则:通过
new Map(prevMapState)创建了一个全新的Map实例,基于旧状态复制出一个新的引用对象,所有修改操作都在新对象上进行,不会影响原状态。 - 引用变化触发重渲染:最终返回的是新的Map实例,和旧状态的引用完全不同,React浅比较后识别到状态变化,就会触发UI重渲染。
- 确保拿到最新状态:函数式更新的参数
prevMapState是React提供的最新状态值,避免了闭包导致的旧状态引用问题,保证更新逻辑基于最新数据执行。
内容的提问来源于stack exchange,提问作者Dolphin
相关产品推荐
相关产品推荐

