在ReactJS中正确修改状态数组


416

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

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

我担心,就地修改阵列push可能会引起麻烦-是否安全?

制作数组副本的另一种选择setState似乎是浪费的。


9
我认为您可以使用React不变性助手。看到这个:facebook.github.io/react/docs/update.html#simple-push
nilgun 2014年

状态数组中的setState检查我的解决方案。<br/> <br> stackoverflow.com/a/59711447/9762736
Rajnikant Lodhi

Answers:


763

阵营的文档说:

将this.state视为不可变的。

push将直接更改状态,即使您此后再次“重置”状态,也有可能导致易于出错的代码。F.ex,它可能导致某些生命周期方法(如componentDidUpdate不会触发)。

在更高版本的React中,建议的方法是在修改状态以防止竞争情况时使用updater函数:

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

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

早期React版本的替代语法

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

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

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

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

8
您能否提供一个示例,说明何时发生竞争状况?
2015年

3
@Qiming push返回新的数组长度,因此它将不起作用。同样,setStateasync和React可以将多个状态更改排队到一个渲染通道中。
David Hellsing

2
@mindeavor说您有一个animationFrame,它在this.state中查找参数,另一种方法是在状态变化时更改一些其他参数。因为setState是异步的,所以可能存在一些状态已更改但未反映在侦听更改的方法中的框架。
David Hellsing,2016年

1
@ChristopherCamps此答案不鼓励调用setState两次,它显示了两个类似的设置状态数组的示例,但不直接对其进行更改。
David Hellsing

2
现在,将状态数组视为不可变的一种简单方法是:let list = Array.from(this.state.list); list.push('woo'); this.setState({list});当然,修改您的样式首选项。
基准

133

如果使用,最简单ES6

initialArray = [1, 2, 3];

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

新数组将是 [1,2,3,4]

React中更新您的状态

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

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


@Muzietto您能详细说明吗?
StateLess

4
这个问题的症结在于改变React状态,而不是修改数组。您一个人看了我的观点并编辑了答案。这使您的答案具有针对性。做得好。
Marco Faustinelli

2
您的问题没有直接关系的OP问题
无国籍

1
@ChanceSmith:StateLess答案中也需要它。不要在状态更新中依赖状态本身。官方文档:reactjs.org/docs/…–
arcol,

1
@RayCoder日志并检查arrayvar的值,看起来它不是数组。
StateLess

57

最简单的方法是ES6

this.setState(prevState => ({
    array: [...prevState.array, newElement]
}))

抱歉,在我的情况下,我想将数组推入数组。tableData = [['test','test']]后推我的新数组tableData = [['test','test'],['new','new']]。如何推这个@David和@Ridd
Johncy 18/09/21

@Johncy如果您想[['test','test'],['new','new']]尝试一下:this.setState({ tableData: [...this.state.tableData, ['new', 'new']]
Ridd

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]);,实现了我想要的东西。但是我认为它不是正确的方式。
约翰西

26

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的输出,作为输出示例,说明如果输入错误会发生什么。

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

越野车onClick只会添加“第三”,而固定的onClick将按预期添加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"]}}) )
Albizia

1
@Albizia我认为您应该找到一个同事并与他们讨论。如果仅进行一个setState调用,则没有批处理问题。关键是要显示React批处理进行更新,所以是的……确实有一种情况,您可以在上述代码的JSBin版本中找到这种情况。该线程中几乎所有答案都无法解决此问题,因此那里会有很多有时会出错的代码
NealeU

state.array = state.array.concat([4])这会改变先前的状态对象。
Emile Bergeron,

1
@EmileBergeron感谢您的坚持。最终,我回头看了看我的大脑拒绝看的内容,并检查了文档,因此我将进行编辑。
NealeU

1
好!因为JS中的不变性不是显而易见的,所以很容易出错(在处理库的API时更是如此)。
Emile Bergeron


3

如果您正在使用功能组件,请按以下方式使用它。

const [chatHistory, setChatHistory] = useState([]); // define the state

const chatHistoryList = [...chatHistory, {'from':'me', 'message':e.target.value}]; // new array need to update
setChatHistory(chatHistoryList); // update the state

1

对于将新元素添加到数组中,push()应该是答案。

对于remove元素和array的更新状态,以下代码对我有用。splice(index, 1)无法工作。

const [arrayState, setArrayState] = React.useState<any[]>([]);
...

// index is the index for the element you want to remove
const newArrayState = arrayState.filter((value, theIndex) => {return index !== theIndex});
setArrayState(newArrayState);

0

我试图在数组状态中推送值并设置这样的值,并通过map函数定义状态数组并推送值。

 this.state = {
        createJob: [],
        totalAmount:Number=0
    }


 your_API_JSON_Array.map((_) => {
                this.setState({totalAmount:this.state.totalAmount += _.your_API_JSON.price})
                this.state.createJob.push({ id: _._id, price: _.your_API_JSON.price })
                return this.setState({createJob: this.state.createJob})
            })

0

这对我来说可以在数组中添加数组

this.setState(prevState => ({
    component: prevState.component.concat(new Array(['new', 'new']))
}));

0

这是我认为可以帮助他人的2020年Reactjs Hook示例。我正在使用它向Reactjs表添加新行。让我知道我是否可以有所改善。

向功能状态组件添加新元素:

定义状态数据:

    const [data, setData] = useState([
        { id: 1, name: 'John', age: 16 },
        { id: 2, name: 'Jane', age: 22 },
        { id: 3, name: 'Josh', age: 21 }
    ]);

有一个按钮触发一个添加新元素的功能

<Button
    // pass the current state data to the handleAdd function so we can append to it.
    onClick={() => handleAdd(data)}>
    Add a row
</Button>
function handleAdd(currentData) {

        // return last data array element
        let lastDataObject = currentTableData[currentTableData.length - 1]

        // assign last elements ID to a variable.
        let lastID = Object.values(lastDataObject)[0] 

        // build a new element with a new ID based off the last element in the array
        let newDataElement = {
            id: lastID + 1,
            name: 'Jill',
            age: 55,
        }

        // build a new state object 
        const newStateData = [...currentData, newDataElement ]

        // update the state
        setData(newStateData);

        // print newly updated state
        for (const element of newStateData) {
            console.log('New Data: ' + Object.values(element).join(', '))
        }

}

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

2
添加一些解释,以避免此帖子被视为低质量帖子stackoverflow
Harsha Biyani,

2
@Kobe在ES6中将此调用称为“扩展运算符”,并将array2项追加到array1。感谢downvote(:
M.Samiei '19

@ M.Samiei您需要编辑您的帖子,以避免投票不足。它只是一个代码段,质量很低。同样,以这种方式修改状态是危险的,因为可能会分批更新,因此首选方式是例如setState(state => ({arrayvar: [...state.arrayvar, ...newelement]}) );
NealeU

无论如何,将newelement对象散布在数组中都会抛出异常TypeError
Emile Bergeron,

-1
//------------------code is return in typescript 

const updateMyData1 = (rowIndex:any, columnId:any, value:any) => {

    setItems(old => old.map((row, index) => {
        if (index === rowIndex) {
        return Object.assign(Object.assign({}, old[rowIndex]), { [columnId]: value });
    }
    return row;
}));

-6

该代码对我有用:

fetch('http://localhost:8080')
  .then(response => response.json())
  .then(json => {
    this.setState({mystate: this.state.mystate.push.apply(this.state.mystate, json)})
  })

2
仍然,您直接
更改

2
并且由于.push返回数字而不是数组,因此无法正确更新组件的状态。
Emile Bergeron
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.