React状态中嵌套对象数组键重命名报错及解决方法问询
解决Redux嵌套数组键重命名的只读属性错误
问题原因
你遇到的错误是因为Redux的状态对象是不可变(只读)的,尤其是使用Redux Toolkit时,状态会被自动冻结(Object.freeze)。直接修改从useSelector获取的原对象(比如给原对象的children赋值)会触发只读属性报错——你不能直接修改Redux存储的原始状态。
正确实现方案
核心思路是:创建新的对象和数组完成键重命名,完全不触碰原Redux状态。写一个纯递归转换函数,配合useMemo缓存结果避免重复计算。
1. 纯递归转换函数
这个函数会遍历每个分类,创建新对象重命名键,递归处理嵌套的children:
const transformCategories = (categories) => { // 处理空数组或非数组的边界情况 if (!categories || !Array.isArray(categories)) return []; return categories.map(category => { // 解构原对象,重命名目标键,保留其他属性 const { _id: value, name: label, children, ...restProps } = category; const transformedItem = { value, label, ...restProps }; // 递归处理children,生成新的children数组 if (children && children.length > 0) { transformedItem.children = transformCategories(children); } return transformedItem; }); };
2. 在组件中使用(配合useMemo优化)
不需要用useEffect执行转换,直接在useSelector获取原始数据后,用useMemo缓存转换结果,只有当原始分类数据变化时才重新计算:
import { useSelector, useMemo } from 'react'; function CategoryComponent() { // 从Redux获取原始只读状态 const rawCategories = useSelector(state => state.categories); // 缓存转换后的结果,避免每次渲染重复执行递归 const transformedCategories = useMemo(() => { return transformCategories(rawCategories); }, [rawCategories]); // 后续直接使用transformedCategories即可 return ( <ul> {transformedCategories.map(item => ( <li key={item.value}> {item.label} {item.children && ( <ul>{/* 递归渲染子分类逻辑 */}</ul> )} </li> ))} </ul> ); }
为什么不用useEffect?
useEffect用于处理副作用(比如请求数据、操作DOM),而数据转换是纯计算逻辑,放在useMemo里更符合React设计理念,还能避免不必要的重渲染和状态修改风险。
内容的提问来源于stack exchange,提问作者Sunil Shrestha
相关产品推荐
相关产品推荐

