каков предпочтительный способ изменить состояние React?


97

Скажем, у меня есть список простых объектов, this.state.listкоторые я затем могу использовать для рендеринга списка потомков. В какой же тогда правильный способ вставить объект this.state.list?

Ниже приведен единственный способ, который, я думаю, будет работать, потому что вы не можете this.stateнапрямую изменять, как указано в документе.

this._list.push(newObject):
this.setState({list: this._list});

Мне это кажется некрасивым. Есть ли способ лучше?


возможный дубликат Правильная модификация массивов состояний в ReactJS , запрос слияния
— Brigand

Все эти ответы касаются добавления элемента в массив. Но как насчет удаления из него элемента? Или просто полностью сбросить массив на новый?
— Августин Ридингер,

Ответы:


165

concat возвращает новый массив, поэтому вы можете сделать

this.setState({list: this.state.list.concat([newObject])});

другая альтернатива - помощник неизменяемости React

  var newState = React.addons.update(this.state, {
      list : {
        $push : [newObject]
      }
  });

  this.setState(newState);

6
concatпринимает массив, поэтому вы захотите this.state.list.concat([newObject]).
— Софи Альперт

@BenAlpert у меня работает в Chrome, вы говорите, что это нестандартное поведение?
— Куча

6
@Heap Ну, если вы передадите ему не-массив, concatон обернет его в массив, но лучше добавить массив самостоятельно, чтобы быть явным: если у вас есть [[1,2], [3,4]]и вы хотите добавить [5,6]в качестве следующего элемента, вам нужно сделать .concat([[5,6]])еще получится [[1,2], [3,4], 5, 6].
— Софи Альперт

2
Насколько я ценю идею помощников неизменяемости (и я могу в конечном итоге использовать ее), Array.concatконечно, лучше добавить еще одну библиотеку.
— Шон Эркхарт 02

1
Вызов this.setState со значением, производным от this.state, приведет к возникновению проблем с пакетной обработкой обновлений. См. Stackoverflow.com/a/41445812/1998186, который ссылается на jsfiddle, показывающий возникшую у вас проблему.
— NealeU 03

40

setState () можно вызвать с функцией в качестве параметра:

this.setState((state) => ({ list: state.list.concat(newObj) }))

или в ES5:

this.setState(function(state) {
  return {
   list: state.list.concat(newObj)
  }
})

2
@Arian: React выполнит необходимые инструкции по мутации DOM, необходимые для отображения только вновь добавленных элементов. При условии, конечно, что вы недавно добавили элементы, они не меняют способ рендеринга предыдущих элементов.
— Хосе Браун

1
Это фантастический ответ, и он кажется намного чище, чем объединение массива просто для выполнения push, хотя я думаю, что это мнение.
— Мэтт Стайлс,

2
@Nyalab, разве вы не должны заключать тело функции в скобки? Как сейчас в вашем примере, фигурные скобки после жирной стрелки будут начинать блок, а не объект. Что-то вроде этого:this.setState((state) => ({ list: state.list.push(newObj) }))
— kumarharsh

3
Как работает этот код? метод push вернет a NUMBER, который будет установлен для listпеременной
— kumarharsh

1
Вы также можете использовать знак:this.setState((state) => ({ list: [...state.list, newObj] }))
— амеб


7

Из документации по реакции ( https://facebook.github.io/react/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous ):

Поскольку this.props и this.state могут обновляться асинхронно, вам не следует полагаться на их значения при вычислении следующего состояния.

Поэтому вам следует сделать это:

this.setState((prevState) => ({
  contacts: prevState.contacts.concat([contact])
}));
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.