redux 使用Object.assign对对象所做的更改更改源对象

2ekbmq32  于 2023-03-30  发布在  其他
关注(0)|答案(5)|浏览(168)

我在react-redux应用程序中有以下reducer代码:

case 'TOGGLE_CLIENT_SELECTION':
        const id = action.payload.id;
        let newState = Object.assign({}, state);
        newState.list.forEach((client) => {
            if (client.id == id) client.selected = !client.selected;
        });
        console.log(state.list[0].selected + ' - ' + newState.list[0].selected)
        return newState;

如果我理解正确的话- Object.assign创建了一个新的对象,但是console.log显示“true - true”或“false - false”。有什么想法吗?为什么它会这样?我如何避免这种行为?

0md85ypi

0md85ypi1#

Object.assign({}, ...)创建浅克隆

Object.assign({}, state)将从state复制属性。字符串,数字和布尔属性是“按值”复制的,而对象属性(包括日期)是“按引用”复制的,这意味着新属性指向与旧属性相同的对象。
例如,给定:

const otherState = { count: 10 };

const state = {
  text: "foo",
  otherState: otherState,
};

const newState = Object.assign({}, state);

newState.text = "bar";
newState.otherState.count = 9;

newState.text将被分配一个全新的字符串,状态不会受到影响。但是statenewState引用的是otherState的同一个示例,所以当你将count更新为9时,state.otherState.countnewState.otherState.count都会受到影响:

state = {
  text = "foo"
  otherState ──────┐
}                  │
                   │
                   │
                   ├────> { count = 9 }
                   │
newState = {       │
  text = "bar"     │
  otherState ──────┘
}

使用Object.assign时,请使用三个规则:如果你到达第三个属性,你现在是在“共享”状态下工作:

newState.otherState.count = 0;
//  ^        ^        ^
//  ╵        ╵        ╵
//  1        2        3

JSON.parse来拯救(?)

Tim建议的一个快速简单的解决方法是使用JSON.stringify:

let newState = JSON.parse(JSON.stringify(state));

但这并不是万无一失的。它适用于一些简单的场景,但有很多场景可能会失败。例如,循环引用会破坏JSON.stringify:

let x = { id: "x" },
y = { id: "y" };
x.y = y;
y.x = x;

// fails with: "Uncaught TypeError: Converting circular structure to JSON"
let cloneX = JSON.parse(JSON.stringify(x));

拯救自定义克隆代码(?)

如果您知道要克隆的结构,那么您可以使用自己的逻辑来处理复制,而不会陷入无休止的循环:

function cloneX( x ){
    const c_x = { id: x.id };
    const c_y = { id: x.y.id };
    c_x.y = c_y;
    c_y.x = c_x;
    return c_x;
}

let x = { id: "x" },
    y = { id: "y" };
x.y = y;
y.x = x;

let x2 = cloneX(x);
x2.y.id = "y2";

console.log( `x2.y.id was updated: ${x2.y.id}` );
console.log( `x.y.id is unchanged: ${x.y.id}` );

也可以想象,通过创造性地使用WeakMap,您可以提出一些逻辑,通过跟踪递归和允许深度复制来处理未知的数据结构。

NPM出手相救(?)

不过,只使用use a library可能更容易。

sycxhyv7

sycxhyv72#

是的,但这不是深度复制。
新对象包含对旧列表的引用。
这里有一个技巧来解决这个问题(有些人可能会说还有更多“正确”的方法):
JSON.字符串化原始的。然后JSON.解析那个字符串。新的对象将是一个“深度”副本(不确定这是否真的是技术上的“深度复制”)。它工作得很好,除非你的子类型比标准的普通旧JSON可接受的东西更复杂。

afdcj2ne

afdcj2ne3#

试试这个:let newState = state.map(item => Object.assign({}, ...item))这将创建一个新对象,而不引用旧对象state

ecbunoof

ecbunoof5#

您可以使用以下命令创建一个深拷贝,然后使用Object.asign

const x = { id: "x" },
y = { id: "y" };
x.y = y;
y.x = x;
const cloneX = Object.fromEntries(Object.entries(x));

const cloneChangeX = Object.assign(cloneX, {z: {id: "z"}});

console.log('const x = ', x, '\nconst cloneX = ', cloneX, '\nconst cloneChangeX = ', cloneChangeX);

最后,您可以执行以下操作:

const cloneChangeX = Object.assign(
  Object.fromEntries(Object.entries(x)),
  { changekey: changevalue }
);

相关问题