React-изменение состояния без использования setState: должно избегать его?

мой код работает, но у меня есть вопрос лучшей практики: у меня есть массив объектов в состоянии, и взаимодействие с пользователем изменит значение одного объекта за раз. Насколько я знаю, я не должен изменять состояние напрямую, я всегда должен использовать setState вместо. Если я хочу избежать этого любой ценой, я глубоко клонирую массив итерацией и изменю клон. Затем установите состояние клона. На мой взгляд, избегая менять состояние, которое я изменю позже в любом случае это просто снижает мою производительность.

Полная версия: этот.государство.данные-это массив объектов. Он представляет собой список тем в форуме, и любимая кнопка будет переключаться, вызывая clickCollect(). Поскольку у меня есть массив в состоянии, когда я изменяю свойство is_collected одного элемента, мне нужно создать копию массива для работы, и после изменения на новое значение я могу установить его в состояние.

var data = this.state.data.slice(0);
data[index].is_collected = !data[index].is_collected;
this.setState({data: data});

var data = this.state.data : Это скопирует указатель на массив и push(), shift () и т. д. изменит состояние напрямую. Оба!--8--> и this.state.data будут затронуты.

var data = this.state.data.slice(0): это делает мелкий клон, push и shift не изменяет состояние, но в моем клоне у меня все еще есть указатели на элементы массива состояния. Так что если я изменюсь data[0].is_collected, this.state.data[0].is_collected получает также изменен. Это происходит до того, как я звоню setState().

как правило, я do:

var data = []; 
for (var i in this.state.data) {
    data.push(this.state.data[i]); 
}

затем я изменяю значение в индексе, устанавливая его в true, когда это false или false, когда это true:

data[index].is_collected = !data[index].is_collected;

и изменения состояния:

this.setState({data: data});

считайте, что мой массив относительно большой или чрезвычайно большой, я думаю, что эта итерация уменьшит производительность моего приложения. Я бы заплатил эту цену, если бы знал, что это правильный путь по любой причине. Однако, в этой функции (clickCollect) I всегда устанавливайте новое значение в состояние, я не жду ложного ответа API, который сказал бы, чтобы остановить внесение изменений. Во всех случаях новое значение будет входить в состояние. Практически я зову setState только для UI, чтобы снова оказать. Итак, вопросы:

  1. должен ли я создать глубокий клон в этом случае? (for var i in ...)
  2. если нет, имеет ли смысл сделать мелкий клон (.slice(0)), если мой массив содержит объекты? Изменения вносятся на объекты внутри массива, поэтому мелкий клон все еще изменяет мое состояние, как и копия (data = this.state.data) будет делать.

мой код упрощен, а вызовы API вырезаны для простоты.

это вопрос новичка, поэтому также приветствуется совершенно другой подход. Или ссылки на другие Q & А.

import React from 'react';

var ForumList = React.createClass({
  render: function() {
      return <div className="section-inner">
        {this.state.data.map(this.eachBox)}
      </div>
  },
  eachBox: function(box, i) {
    return <div key={i} className="box-door">
        <div className={"favorite " + (box.is_collected ? "on" : "off")} onTouchStart={this.clickCollect.bind(null, i)}>
          {box.id}
        </div>
    </div>
  },
  getInitialState: function() {
    return {data: [
      {
        id: 47,
        is_collected: false
      },
      {
        id: 23,
        is_collected: false
      },
      {
        id: 5,
        is_collected: true
      }
    ]};
  },
  clickCollect: function(index) {
    var data = this.state.data.slice(0);
    data[index].is_collected = !data[index].is_collected;
    this.setState({data: data});
  }
});

module.exports = ForumList;

4 ответов


лично я не всегда следую правилу, если вы действительно понимаете, что вы пытаетесь сделать, то я не думаю, что это проблема.

var data = this.state.data.slice(0);
data[index].is_collected = !data[index].is_collected;
this.setState({data: data});

в этом случае мутирующее состояние и вызов setState снова, как это прекрасно

this.state.data[index].is_collected = !this.state.data[index].is_collected;
this.setState({data: this.state.data});

причина, по которой вы должны избегать мутирования своего состояния, заключается в том, что если у вас есть ссылка на this.state.data, а вызов setState несколько раз, вы можете потерять ваши данные:

const myData = this.state.data
myData[0] = 'foo'
this.setState({ data: myData })
// do something...
// ...
const someNewData = someFunc()
this.setState({ data: someNewData })

myData[1] = 'bar' // myData is still referencing to the old state
this.setState({ data: myData }) // you lose everything of `someNewData`

если вы действительно беспокоитесь об этом, просто пойдите для незыблемыми.js


Если вы хотите следовать рекомендациям react, вы должны сделать мелкую копию всего своего массива при изменении любого свойства. Пожалуйста, ознакомьтесь с реализацией" неизменяемой " библиотеки.

но, по моему опыту и по моему мнению, setState метод должен вызываться, если у вас есть реализации" shouldCompomenentUpdate". Если вы думаете, что ваша мелкая копия будет потреблять гораздо больше ресурсов, то реагируйте на виртуальные проверки dom, вы можете сделать это:

this.state.data[0].property = !this.state.data[0].property;
this.forceUpdate();

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

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


Если я правильно понял ваш вопрос, у вас есть массив объектов и когда свойство одного объекта в массиве изменяется,

  1. создайте глубокий клон массива и перейдите в setState
  2. создайте неглубокий клон и перейдите в setState

Я только что проверил с redux пример приложения todo и в случае изменения одного свойства объекта вы должны создать новую копию этого одного объект не весь массив. Я рекомендую вам прочитать о redux и по возможности использовать для управления состоянием вашего приложения.