简体   繁体   English

为什么在 React 中调用“setState()”时使用扩展运算符?

[英]Why use the spread operator when calling 'setState()' in React?

I just start picking up react.js so I went through a lot of tutorials and I've stumbled upon this bit which basically meant to delete an item from the state.我刚开始使用react.js,所以我浏览了很多教程,我偶然发现了这一点,它基本上意味着从状态中删除一个项目。

this is how the guy introduced to me the delete function这就是那家伙向我介绍删除功能的方式

  delTodo = id => {
    this.setState({
      todos: [...this.state.todos.filter(todo => todo.id !== id)]
    });
  };

Since I am not so familiar with javascript I had a hard time figuring out what the ... operator is doing and why exactly is he using it in the given scenario.由于我对 javascript 不太熟悉,我很难弄清楚...运算符在做什么以及他为什么在给定的场景中使用它。 So in order to have a better understanding of how it works, I played a bit in the console and I've realised that array = [...array] .所以为了更好地理解它是如何工作的,我在控制台上玩了一会儿,我意识到array = [...array] But is that true?但这是真的吗? Is this bit doing the same exact thing as the one from above?这个位和上面的那个做同样的事情吗?

  delTodo = id => {
    this.setState({
      todos: this.state.todos.filter(todo => todo.id !== id)
    });
  };

Could someone more experienced clarify to me why he has chosen to be using that approach instead of the one I've come up with?有经验的人可以向我澄清为什么他选择使用这种方法而不是我提出的方法吗?

As .filter gives you a new array (than mutating the source array), it is acceptable and results in the same behaviour, making spreading redundant here.由于.filter为您提供了一个新数组(而不是改变源数组),它是可以接受的并且会导致相同的行为,从而使此处的传播变得多余。

What's not acceptable is:不能接受的是:

const delIndex = this.state.todos.findIndex(todo => todo.id !== id);
this.state.todos.splice(delIndex, 1); // state mutation

this.setState({
  todos: this.state.todos
});

slice is fine though:不过slice很好:

const delIndex = this.state.todos.findIndex(todo => todo.id !== id);

this.setState({
  todos: [
    ...this.state.todos.slice(0, delIndex),
    ...this.state.todos.slice(delIndex + 1)
  ]
});

If you mutate state (which keeps ref same) React may not be able to ascertain which part of your state actually changed and probably construct a tree on next render which is different from expected.如果您改变状态(保持 ref 相同),React 可能无法确定您状态的哪一部分实际发生了变化,并且可能在下一次渲染时构造一个与预期不同的树。

As per the documentation:根据文档:

Never mutate this.state directly, as calling setState() afterwards may replace the mutation you made.永远不要直接改变this.state ,因为之后调用setState()可能会替换你所做的改变。 Treat this.state as if it were immutable .this.state视为immutable


So, in the example from the tutorial you've mentioned, you wouldn't need to make a copy of the array to update your state.因此,在您提到的教程中的示例中,您不需要复制数组来更新您的状态。

// GOOD
delTodo = id => {
  this.setState({
    todos: this.state.todos.filter(...)
  })
}

Array.filter method creates a new array and does not mutate the original array, therefore it won't directly mutate your state. Array.filter方法创建一个新数组并且不会改变原始数组,因此它不会直接改变您的状态。 Same thing applies to methods such as Array.map or Array.concat .同样的事情适用于诸如Array.mapArray.concat

If your state is an array and you're applying methods that are mutable, you should copy your array.如果您的状态是一个数组并且您正在应用可变的方法,则您应该复制您的数组。

See more to figure out which Array methods are mutable:查看更多内容以了解哪些Array方法是可变的:

However, if you were to do something like the following:但是,如果您要执行以下操作:

// BAD
delTodo = id => {
  const todos = this.state.todos
  todos.splice(id, 1)
  this.setState({ todos: todos })
}

Then you'd be mutating your state directly, because Array.splice changes the content of an existing array, rather than returning a new array after deleting the specific item.然后你会直接改变你的状态,因为Array.splice改变了现有数组的内容,而不是在删除特定项目后返回一个新数组。 Therefore, you should copy your array with the spread operator.因此,您应该使用扩展运算符复制您的数组。

// GOOD
delTodo = id => {
  const todos = [...this.state.todos]
  todos.splice(id, 1)
  this.setState({ todos: todos })
}

Similarly with objects , you should apply the same technique.objects类似,您应该应用相同的技术。

// BAD
updateFoo = () => {
  const foo = this.state.foo // `foo` is an object {}
  foo.bar = "HelloWorld"
  this.setState({ foo: foo })
}

The above directly mutates your state, so you should make a copy and then update your state.以上直接改变了你的状态,所以你应该制作一个副本,然后更新你的状态。

// GOOD
updateFoo = () => {
  const foo = {...this.state.foo} // `foo` is an object {}
  foo.bar = "HelloWorld"
  this.setState({ foo: foo })
}

Hope this helps.希望这可以帮助。

Why use the spread operator at all?为什么要使用价差运算符?

The spread operator ... is often used for creating shallow copies of arrays or objects.展开运算符...通常用于创建数组或对象的浅拷贝。 This is especially useful when you aim to avoid mutating values, which is encouraged for different reasons.当您的目标是避免改变值时,这尤其有用,出于不同的原因鼓励这样做。 TLDR; TLDR; Code with immutable values is much easier to reason about.具有不可变值的代码更容易推理。 Long answer here .长答案在这里

Why is the spread operator used so commonly in react?为什么在反应中如此普遍地使用扩展运算符?

In react, it is strongly recommended to avoid mutation of this.state and instead call this.setState(newState) .this.state强烈建议避免this.state而是调用this.setState(newState) Mutating state directly will not trigger a re-render, and may lead to poor UX, unexpected behavior, or even bugs.直接改变状态不会触发重新渲染,并可能导致糟糕的用户体验、意外行为,甚至错误。 This is because it may cause the internal state to differ from the state that is being rendered.这是因为它可能导致内部状态与正在呈现的状态不同。

To avoid manipulating values, it has become common practice to use the spread operator to create derivatives of objects (or arrays), without mutating the original:为避免操作值,使用扩展运算符创建对象(或数组)的派生类已成为常见做法,而不改变原始对象:

// current state
let initialState = {
    user: "Bastian",
    activeTodo: "do nothing",
    todos: ["do nothing"]
}


function addNewTodo(newTodo) {
    // - first spread state, to copy over the current state and avoid mutation
    // - then set the fields you wish to modify
    this.setState({
        ...this.state,
        activeTodo: newTodo,
        todos: [...this.state.todos, newTodo]
    })
}

// updating state like this...
addNewTodo("go for a run")
// results in the initial state to be replaced by this:
let updatedState = {
    user: "Bastian",
    activeTodo: "go for a run",
    todos: ["do nothing", "go for a run"]
}

Why is the spread operator used in the example?为什么在示例中使用了扩展运算符?

Probably to avoid accidental state mutation.可能是为了避免意外的状态突变。 While Array.filter() does not mutate the original array and is safe to use on react state, there are several other methods which do mutate the original array, and should not be used on state.虽然Array.filter()不会改变原始数组并且可以安全地用于反应状态,但还有其他几种方法可以改变原始数组,并且不应该用于状态。 For example: .push() , .pop() , .splice() .例如: .push().pop().splice() By spreading the array before calling an operation on it, you ensure that you are not mutating state.通过在对数组调用操作之前展开数组,可以确保不会改变状态。 That being said, I believe the author made a typo and instead was going for this:话虽如此,我相信作者打错了字,而是打算这样做:

 delTodo = id => {
    this.setState({
      todos: [...this.state.todos].filter(todo => todo.id !== id)
    });
  };

If you have a need to use one of the mutating functions, you can choose to use them with spread in the following manner, to avoid mutating state and potentially causing bugs in your application:如果您需要使用其中一个变异函数,您可以选择通过以下方式将它们与 spread 一起使用,以避免变异状态并可能导致应用程序中的错误:

// here we mutate the copied array, before we set it as the new state
// note that we spread BEFORE using an array method
this.setState({
      todos: [...this.state.todos].push("new todo")
});

// in this case, you can also avoid mutation alltogether:
this.setState({
      todos: [...this.state.todos, "new todo"]
});

The spread operator that the guy's code is applying causes the array returned from the filter function to be copied again.这家伙的代码应用的扩展运算符导致过滤器函数返回的数组再次被复制。

Since [.filter is returning a new array][ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter] , you will already avoid mutating the array in state directly.由于 [.filter 正在返回一个新数组][ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter] ,您已经避免直接在 state 中改变数组. It seems like the spread operator may be redundant.似乎扩展运算符可能是多余的。

One thing I wanted to point out too is while the values in a copied array may be the same as the values in an old array ( array = [...array] ), the instances change so you wouldn't be able to use '===' or '==' to check for strict equivalency.我还想指出的一件事是,虽然复制数组中的值可能与旧数组中的值相同( array = [...array] ),但实例会发生变化,因此您将无法使用'===' 或 '==' 来检查严格的等价性。

const a = ['a', 'b']
const b = [...a]

console.log(a === b) // false
console.log(a == b) // false
console.log(a[0] === b[0]) // true
console.log(a[1] === b[1]) // true

Hope this helps!希望这可以帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM