setState 中数组操作的 concat 和 ES6 扩展运算符的区别(React Hooks)

Difference between concat and ES6 spread operator for array manipulation in setState (React Hooks)

我遇到了这个奇怪的错误,我仍然无法理解到底发生了什么。

我正在更新一个状态数组,并希望在保留旧对象的情况下将新对象解析到该数组。

我使用 ES6 的已知方法是使用 spread 运算符作为:

const [stories, setStories] = useState([]);

...

setStories(stories => [...stories, newStories]);

其中 newStories 也是一个对象数组。

但是我得到以下输出:

console.log(stories)

而当我对 concat 做同样的事情时:

setStories(stories => stories.concat(newStories));

它returns符合预期:

console.log(stories)

到目前为止,我认为这两种方法的效果相同。

我也找到了这篇文章,它也证实了我的知识,并且与我得到的上述输出相矛盾。See: Best solution: Spread operator … & a wrapper

docs 中的这段代码为例:

let parts = ['shoulders', 'knees'];
let lyrics = ['head', ...parts, 'and', 'toes'];
//  ["head", "shoulders", "knees", "and", "toes"]

如您所见,将 parts 数组展开到 lyrics 数组中,它会从数组中提取数据。如果你改为写:

let parts = ['shoulders', 'knees'];
let lyrics = ['head', parts, 'and', 'toes'];
//  ["head", ["shoulders", "knees"], "and", "toes"]

你会得到数组。

如果你取一个数组

[{name: 'story1'}, {name: 'story2'}]

并将它放在你的另一个数组中,你会得到:

setStories((stories) => [...stories, newStories]);
[[{name: 'story1'}, {name: 'story2'}]]

如果你再做一次,你会得到:

setStories((stories) => [...stories, newStories]);
[[{name: 'story1'}, {name: 'story2'}],[{name: 'story1'}, {name: 'story2'}]]

因为您只是将数组放入另一个数组。

如果您想从数组中删除对象,您必须spread将对象放入您的状态。

setStories((prevstate) => [...prevstate, ...newStories]);

然后你得到:

[{},{},{},{}]

因为通过展开,您会将对象从它们当前所在的数组中取出。

作为旁注,我不建议将回调函数的参数命名为与您的状态相同的名称,这会使您对实际引用的内容感到困惑。我见过 prevstate 或者在你的情况下有些人会写 prevStories 等等。只是一个建议。

const a = [{a: 1}, {b: 2}]
const b = [{c: 3}, {d: 4}]

const c = [ ...a, ...b ]
const d = [ a, b ]
const e = [ ...a, b ]
const f = [ a, ...b ]

console.log(c)
console.log(d)
console.log(e)
console.log(f)

您需要将两个数组展开以创建一个新数组

const story1 = [{a: 1}, {b: 2}]
const story2 = [{c: 3}, {d: 4}]

const newStories = [...story1, ...story2];
console.log(newStories);

相反,如果您只将它用于一个数组,它会在 newArray

中创建一个子数组

const story1 = [{a: 1}, {b: 2}]
const story2 = [{c: 3}, {d: 4}]

const newStories = [story1, ...story2];
console.log(newStories);

很简单。 ... 展开运算符将一个数组展开成一个新数组。但是,您的用法不正确:

setStories(stories => [...stories, newStories]); // ❌
setStories(stories => [...stories, ...newStories]); // ✅

第一行不起作用的原因是如果 newStories 是一个数组(它可能是名字),... 是解构和注入所有数组的必要条件项目单独放入新数组。

这更清楚地说明了错误:

const a = [1,2]
const b = [3,4]
const c = [...a, b] // [1,2,[3,4]] ❌
const d = [...a, ...b] // [1,2,3,4] ✅

创建新数组时唯一不使用 ... 展开运算符的情况是添加单个项目时,如下所示:

const a = [1,2]
const b = 5
const c = [...a, ...c] // ❌
const d = [...a, b] // ✅