Правильная модификация массивов состояний в ReactJS

281

Я хочу добавить элемент в конец массива state, это правильный способ сделать это?

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

Я обеспокоен тем, что изменение массива на месте с помощью push может вызвать проблемы - безопасно?

Альтернатива создания копии массива и setState ing, которая кажется расточительной.

Теги:

7 ответов

516
Лучший ответ

Документы React говорят:

Относитесь к этому состоянию как к неизменному.

Ваше push будет напрямую изменять состояние, что потенциально может привести к появлению кода, подверженного ошибкам, даже если впоследствии вы "сбрасываете" состояние снова. F.ex, это может привести к тому, что некоторые методы жизненного цикла, такие как componentDidUpdate сработают.

Рекомендуемый подход в более поздних версиях React заключается в использовании функции обновления при изменении состояний для предотвращения состояний гонки:

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

"Трата" памяти не является проблемой по сравнению с ошибками, с которыми вы можете столкнуться при нестандартных модификациях состояния.

Альтернативный синтаксис для более ранних версий React

Вы можете использовать concat для получения чистого синтаксиса, так как он возвращает новый массив:

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

В ES6 вы можете использовать Spread Operator:

this.setState({
  arrayvar: [...this.state.arrayvar, newelement]
})
  • 7
    Можете ли вы привести пример того, когда может возникнуть состояние гонки?
  • 0
    Обратите внимание, что этот ответ применяется только в том случае, если вы хотите только добавить / удалить элементы. Если вы хотите обновить элементы в массиве, у вас могут возникнуть проблемы, если он содержит объекты. Рассмотрим этот код: var a = [1,2,3, {a:1}]; var b = a.slice(); b[3].a = 2; console.log(a[3].a);
Показать ещё 13 комментариев
99

Самый простой, если вы используете ES6.

initialArray = [1, 2, 3];

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

Новый массив будет [1,2,3,4]

чтобы обновить ваше состояние в Реагировать

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

Узнайте больше о деструкции массива

  • 0
    @Muzietto вы можете уточнить?
  • 4
    Суть этого вопроса - изменение состояния React, а не изменение массивов. Вы сами поняли мою точку зрения и отредактировали свой ответ. Это делает ваш ответ актуальным. Отлично сработано.
Показать ещё 4 комментария
39

Самый простой способ с ES6:

this.setState(prevState => ({
    array: [...prevState.array, newElement]
}))
  • 2
    Это действительно самый крутой ответ.
  • 0
    Извините, в моем случае я хочу вставить массив в массив. tableData = [['test','test']] После того, как tableData = [['test','test'],['new','new']] мой новый массив tableData = [['test','test'],['new','new']] . как протолкнуть это @David и @Ridd
Показать ещё 2 комментария
19

React может пакетно обновлять, и поэтому правильный подход заключается в предоставлении setState с функцией, которая выполняет обновление.

Для дополнения обновления React будет выполнено следующее:

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

или для concat():

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

Ниже показано, что https://jsbin.com/mofekakuqi/7/edit?js,output как пример того, что произойдет, если вы ошибетесь.

Приведение setTimeout() правильно добавляет три элемента, потому что React не будет выполнять пакетные обновления в обратном вызове setTimeout (см. https://groups.google.com/d/msg/reactjs/G6pljvpTGX0/0ihYw2zK9dEJ).

Багги 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'));
17

Как упоминается в примечании @nilgun, вы можете использовать помощники по неизменности. Я нашел, что это очень полезно.

Из документов:

Простой push

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

initialArray все еще [1, 2, 3].

  • 5
    Помощники неизменности React описаны в документации как устаревшие. Теперь вместо него следует использовать github.com/kolodny/immutability-helper .
1

Если вы хотите использовать push, вы можете сделать что-то вроде этого:

// wrong because push modify the array directly
this.setState(prevState => ({
  arrayvar: prevState.arrayvar.push(someElemet) && prevState.arrayvar,
}));

Лучшим решением, конечно, является предотвращение мутации с помощью concat, slice или spread.

Подробнее см. видеоурок.

0

Этот код работает для меня:

fetch('http://localhost:8080')
  .then(response => response.json())
  .then(json => {
    this.setState({mystate: this.state.mystate.push.apply(this.state.mystate, json)})
  })
  • 0
    тем не менее, вы изменяете состояние напрямую

Ещё вопросы

Сообщество Overcoder
Наверх
Меню