React useReducer遇到的小问题及解决方案

在useReducer的reducer中,对state进行操作,就会导致错误

function getAnalyzeItem(){
    return {
        a: 1,
        b: 2
    }
}
// 错误写法  
function analyzeReducer(state: TFilterItemVal[], action: TReducerAction<TFilterItemVal>) {
    const { index, data, type } = action;
    switch (type) {
        case "add":
            state.push(getAnalyzeItem());
            return [...state];
        case "delete":
            typeof index === "number" &&state.splice(index, 1);
            return[...state];
        case "update":
            if (typeof index === "number") {
                state[index] = {
                    ...state[index],
                    ...data,
                }
                return [...state];
            }
        case "init":
            return [getAnalyzeItem()];
        default:
            return state;
    }
}
const [analyzeLats, analyzeDispatch] = useReducer(analyzeReducer, [getAnalyzeItem()]);
// 正确写法    
function analyzeReducer(state: TFilterItemVal[], action: TReducerAction<TFilterItemVal>) {
    const { index, data, type } = action;
    ///重点在这//
    let oldState = [...state];
    /
    switch (type) {
        case "add":
            oldState.push(getAnalyzeItem());
            return oldState;
        case "delete":
            typeof index === "number" && oldState.splice(index, 1);
            return oldState;
        case "update":
            if (typeof index === "number") {
               oldState[index] = {
                    ...oldState[index],
                    ...data,
                };
                return oldState;
            }
        case "init":
            return [getAnalyzeItem()];
        default:
            return state;
    }
}
const [analyzeLats, analyzeDispatch] = useReducer(analyzeReducer, [getAnalyzeItem()]);

这个地方一定要注意,state是一个immutable的,也就是不变的,如果state是一个对象,那么一定要对其做一次深拷贝,在对其进行修改,如果直接进行修改,则会出错。

就比如上面的代码,state原本是一个对象,我直接用push和splice操作后,会发现他一会添加一个,一会添加两个,也就是时而正常,时而不正常。在路上思考了一下,有可能是这个思路reducer中传入的state,在第一次执行的时候,会默认设置为状态值,因为是一个对象,所以会在内存中开辟一个空间#fff存储,之后如果直接去修改state那么就想当与对原始的值进行了修改,所以在后续的操作中会拿出上一次的state来作为依据,继续修改。举个例子 在页面还在加载 数据时,也就是updateQueue还没有空的情况下,我去 直接修改state,会正常显示,当页面稳定了,渲染完毕后,再去直接修改state,就会添加两个或删除两个。为什么会出现这种情况,我的猜测如下:
1. 因为修改setState在react内部是通过queue来统一修改的,所以会导致多次setState进行合并后处理,所以当渲染还没结束,我就修改了state,那么它可能会将两次dispatch(根据redux中的理解,他在useReducer的时候也会执行一次dispatch,只不过传的type是一个固定值,会默认走到default里),后面又来了一个dispatch({type: “add”}),就会将这个和初始化的合并,不会返回默认的state,而是修改后的state,也就是两个getAnalyzeItem()
2. 当他渲染完毕之后,state已经确认了,如果再次调用dispatch({type: “add”}),对state进行push一个新的值,会先将它的原始值进行了修改,之后,它会进入updateQueue中等待执行,执行的时候,会取出老的state(此时已经被修改过了),再去执行一次push,并返回一个新的state,然后开始进行渲染。这也就导致了点一个添加,却加了两个的问题。

解决方法:
1. 使用immer来对对象进行操作(跟解构其实很像,单独浅复制出来一个对象,然后一层层向下找,找到那个对应的元素,然后深拷贝一个,然后进行修改,形成新的对象。在新的对象里,除了那个被修改的元素,其他元素与之前的元素相同)

let obj = {
	a: { aa: { aaa: 1 }, ab: { aba: 2 } },
	b: { bb: 2 },
	c: { cc: { ccc: { cccc: 3 } } },
};
let newObj = produce(obj, (prevState) => {
	prevState.a.aa.aaa = 2;
});
console.log(obj.a.ab === newObj.a.ab);	// true
console.log(obj.a, newObj.a);			// false 因为他其中一个子元素被修改了
console.log(obj.b === newObj.b, newObj.c === obj.c);	//  true true
2. 解构出来(如果层级比较深,需要一层一层的结构,就像剥洋葱,可能会剥出眼泪)
3. 自己写一个深拷贝(会创建一个全新的对象,每个节点都 不一样,性能消耗较大,推荐指数**)

你可能感兴趣的:(笔记,js,react)