React.js 中状态数组的正确修改

IT技术 javascript reactjs
2021-02-08 00:13:34

我想在state数组的末尾添加一个元素,这是正确的方法吗?

this.state.arrayvar.push(newelement);
this.setState({ arrayvar:this.state.arrayvar });

我担心就地修改数组push可能会导致麻烦 - 安全吗?

制作数组副本的替代方法,而setStateing 似乎很浪费。

6个回答

阵营的文档说:

将 this.state 视为不可变的。

push将直接改变状态,这可能会导致容易出错的代码,即使您之后再次“重置”状态。F.ex,它可能导致某些生命周期方法componentDidUpdate不会触发。

在更高版本的 React 中推荐的方法是在修改状态时使用更新程序函数来防止竞争条件:

this.setState(prevState => ({
  arrayvar: [...prevState.arrayvar, newelement]
}))

与使用非标准状态修改可能面临的错误相比,内存“浪费”不是问题。

早期 React 版本的替代语法

您可以使用concat获得干净的语法,因为它返回一个新数组:

this.setState({ 
  arrayvar: this.state.arrayvar.concat([newelement])
})

在 ES6 中,您可以使用扩展运算符

this.setState({
  arrayvar: [...this.state.arrayvar, newelement]
})
您能否提供一个何时会发生竞争条件的示例?
2021-03-10 00:13:34
@mindeavor 说你有一个 animationFrame 寻找 this.state 中的参数,还有另一个方法在状态改变时改变一些其他参数。可能有一些帧的状态已更改但未反映在侦听更改的方法中,因为 setState 是异步的。
2021-03-13 00:13:34
如今,将状态数组视为不可变的一种简单方法是:let list = Array.from(this.state.list); list.push('woo'); this.setState({list});当然要根据您的风格偏好进行修改。
2021-03-21 00:13:34
@Qimingpush返回新的数组长度,这样就行不通了。此外,setState是异步的,React 可以将多个状态更改排队到一个渲染通道中。
2021-03-26 00:13:34
@ChristopherCamps 这个答案不鼓励调用setState两次,它显示了设置状态数组而不直接改变它的两个类似示例。
2021-03-31 00:13:34

最简单,如果您使用ES6.

initialArray = [1, 2, 3];

newArray = [ ...initialArray, 4 ]; // --> [1,2,3,4]

新阵列将是 [1,2,3,4]

React 中更新你的状态

this.setState({
  arrayvar:[...this.state.arrayvar, newelement]
});

了解有关数组解构的更多信息

@RayCoder 记录并检查arrayvar 的值,看起来它不是数组。
2021-03-17 00:13:34
@ChanceSmith:StateLes答案中也需要它不要依赖于状态本身的状态更新。官方文档:reactjs.org/docs/...
2021-03-18 00:13:34
您的问题不直接涉及 OP 问题
2021-03-26 00:13:34
@Muzietto 你能详细说明一下吗?
2021-03-28 00:13:34
这个问题的关键是改变 React 状态,而不是修改数组。您自己看到了我的观点并编辑了您的答案。这使您的答案具有相关性。做得好。
2021-04-01 00:13:34

最简单的方法ES6

this.setState(prevState => ({
    array: [...prevState.array, newElement]
}))
@Johncy 如果您想[['test','test'],['new','new']]尝试:this.setState({ tableData: [...this.state.tableData, ['new', 'new']]
2021-03-13 00:13:34
抱歉,就我而言,我想将数组推入数组。tableData = [['test','test']]推我的新阵列后tableData = [['test','test'],['new','new']]如何推送这个@David 和@Ridd
2021-03-31 00:13:34
this.setState({ tableData: [...this.state.tableData ,[item.student_name,item.homework_status_name,item.comments===null?'-':item.comments] ] });它两次插入新数组 this.state.tableData.push([item.student_name,item.homework_status_name,item.comments===null?'-':item.comments]);它实现了我想要的东西。但这不是我认为的正确方式。
2021-04-06 00:13:34

React 可能会批量更新,因此正确的方法是为 setState 提供一个执行更新的函数。

对于 React 更新插件,以下内容将可靠地工作:

this.setState( state => update(state, {array: {$push: [4]}}) );

或对于 concat():

this.setState( state => ({
    array: state.array.concat([4])
}));

下面以https://jsbin.com/mofekakuqi/7/edit?js,output为例说明如果出错会发生什么。

setTimeout() 调用正确添加了三个项目,因为 React 不会在 setTimeout 回调中批量更新(参见https://groups.google.com/d/msg/reactjs/G6pljvpTGX0/0ihYw2zK9dEJ)。

有问题的 onClick 只会添加“Third”,但固定的会按预期添加 F、S 和 T。

class List extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      array: []
    }

    setTimeout(this.addSome, 500);
  }

  addSome = () => {
      this.setState(
        update(this.state, {array: {$push: ["First"]}}));
      this.setState(
        update(this.state, {array: {$push: ["Second"]}}));
      this.setState(
        update(this.state, {array: {$push: ["Third"]}}));
    };

  addSomeFixed = () => {
      this.setState( state => 
        update(state, {array: {$push: ["F"]}}));
      this.setState( state => 
        update(state, {array: {$push: ["S"]}}));
      this.setState( state => 
        update(state, {array: {$push: ["T"]}}));
    };



  render() {

    const list = this.state.array.map((item, i) => {
      return <li key={i}>{item}</li>
    });
       console.log(this.state);

    return (
      <div className='list'>
        <button onClick={this.addSome}>add three</button>
        <button onClick={this.addSomeFixed}>add three (fixed)</button>
        <ul>
        {list}
        </ul>
      </div>
    );
  }
};


ReactDOM.render(<List />, document.getElementById('app'));
真的有这种情况发生吗?如果我们简单地做this.setState( update(this.state, {array: {$push: ["First", "Second", "Third"]}}) )
2021-03-23 00:13:34
@Albizia 我认为您应该找一位同事并与他们讨论。如果您只进行一次 setState 调用,则不存在批处理问题。重点是要说明React是批量更新的,所以是的...确实有一个案例,就是你可以在上面代码的JSBin版本中找到的。这个线程中几乎所有的答案都没有解决这个问题,所以会有很多代码有时会出错
2021-03-27 00:13:34
@EmileBergeron 感谢您的坚持。我最终回过头来,看到了我的大脑拒绝看到的内容,并检查了文档,所以我会进行编辑。
2021-03-31 00:13:34
state.array = state.array.concat([4]) 这会改变之前的状态对象。
2021-04-02 00:13:34
好的!很容易出错,因为 JS 中的不变性并不明显(在处理库的 API 时更是如此)。
2021-04-03 00:13:34

正如评论中提到的@nilgun,您可以使用 react immutability helpers我发现这非常有用。

从文档:

简单推送

var initialArray = [1, 2, 3];
var newArray = update(initialArray, {$push: [4]}); // => [1, 2, 3, 4]

initialArray 仍然是 [1, 2, 3]。

React 不变性助手在文档中被描述为已弃用。 现在应该改用github.com/kolodny/immutability-helper
2021-04-06 00:13:34