ReactJS中forEach遍历给state赋值仅保留最后一项的原因及解决方法
问题成因
- React 的状态更新是异步批量执行的:在
useEffect回调的完整执行周期内,所有setDisabledCategories调用会被暂存,等到整个递归逻辑执行完成后才会统一触发更新。你在循环内每次调用setDisabledCategories([...disabledCategories, category])时,拿到的disabledCategories始终是当前渲染周期的初始值(空数组),相当于每次都是往空数组里添加当前项,最终只有最后一次的更新结果会被保留。 - 闭包捕获旧值问题:
notActiveCategories函数定义时捕获了当前渲染周期的disabledCategories初始值,递归过程中状态并没有实时更新,所以所有的状态更新操作都是基于同一个初始空数组执行的,没有叠加之前的更新结果。
解决方案
推荐优先使用「先批量收集符合条件的项,再一次性更新状态」的方案,性能更好,也避免闭包问题:
const [disabledCategories, setDisabledCategories] = useState([]); // 递归收集所有禁用分类,返回完整的结果数组 function getDisabledCategories(categories) { const result = []; categories.forEach(category => { // 递归遍历子分类 if (category.isActive && Array.isArray(category.children)) { result.push(...getDisabledCategories(category.children)); } // 收集当前禁用分类 if (!category.isActive) { result.push(category); } }); return result; } useEffect(() => { const disabledList = getDisabledCategories(CategoriesData); setDisabledCategories(disabledList); console.log(disabledList); // 符合预期的3个分类项 }, []);
如果你需要在遍历过程中实时更新状态,可以使用 setState 的函数式更新形式,直接拿到最新的状态值进行修改:
function notActiveCategories(categories) { categories.forEach((category) => { if (category.isActive && Array.isArray(category.children)) { notActiveCategories(category.children); } if (!category.isActive) { // 函数式更新,prev 始终是最新的状态值 setDisabledCategories(prev => [...prev, category]); } }); };
注意:该方案会多次触发状态重渲染,性能低于批量收集后更新的方案,非特殊场景不推荐使用。
内容的提问来源于stack exchange,提问作者Andrew
相关产品推荐
相关产品推荐

