Почему React обновляется при прямом изменении состояния? - PullRequest
3 голосов
/ 19 июня 2020

В приведенном ниже примере я использую карту ES6 в качестве значения состояния в React:

class App extends React.Component {
  constructor(props) {
    super(props);
    const results = new Map();
    results["group1"] = [{ value: "..." }, { value: "..." }];
    this.state = { results };
  }

  onUpdateClick(i) {
    this.state.results["group1"][i].value = i;
    this.setState({});
  }

  onResetClick(i) {
    this.state.results["group1"][i].value = "...";
    this.setState({});
  }

  render() {
    const { results } = this.state;
    return (
      <div>
        {results["group1"].map((r, i) => (
          <div>
            {r.value}&nbsp;
            <button onClick={e => this.onUpdateClick(i)}>update</button>
            <button onClick={e => this.onResetClick(i)}>reset</button>
          </div>
        ))}
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("container"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id='container'></div>

Когда вы нажимаете кнопку, я напрямую обновляю карту на месте, а затем вызываю setState без аргументов. Я не делаю клон / глубокую копию карты. Основываясь на моем понимании документации React, это не должно работать, и об этом прямо предупреждают в документах:

Никогда не изменяйте this.state напрямую, поскольку вызов setState () впоследствии может заменить сделанную вами мутацию. Относитесь к this.state как к неизменяемому

(https://reactjs.org/docs/react-component.html#state)

В документации также указано, что сравнения неглубокие, поэтому вызов с пустым объектом обязательно не должно привести к слиянию и, следовательно, к повторному рендерингу?

Почему этот пример работает?

(также я должен отметить, что я воспроизвожу это поведение с React v16.9.0)

Изменить: я также хочу указать (поскольку многие ответы относятся к тому факту, что я передаю пустой объект), что компонент повторно визуализируется (и обновляется), если я вызываю setState следующим образом:

this.setState({ results: this.state.results })

, что не должно вызывать повторного рендеринга

Ответы [ 4 ]

2 голосов
/ 19 июня 2020

Из документации: setState

setState () всегда будет приводить к повторному рендерингу, если только shouldComponentUpdate () не вернет false.

Это означает, что this.setState({}); приводит к повторной визуализации независимо от того, передаете ли вы изменение в качестве аргумента или нет

1 голос
/ 20 июня 2020

Согласно документации, setState () всегда будет приводить к повторному рендерингу , если shouldComponentUpdate () не вернет false. Вы можете думать о функции render () как о создании дерева элементов React . При следующем обновлении состояния или свойств эта функция render () вернет другое дерево элементов React ( Virtual DOM ).

В любой момент времени библиотека React поддерживает две копии виртуальной модели DOM .

Когда запускается запрос к setState (), React создает новое дерево , содержащее реактивные элементы в компоненте (вдоль с обновленным состоянием ). Это дерево используется для определения того, как пользовательский интерфейс компонента должен измениться в ответ на изменение состояния, путем сравнения его с элементами предыдущего дерева.

Виртуальная модель DOM, которая затем синхронизируется с реальной DOM посредством процесса, называемого согласованием .

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

Refs-

1 голос
/ 19 июня 2020

Вы ошибаетесь:

this.setState({}); // this will do nothing.

Вы ожидаете, что состояние должно быть обновлено с помощью пустого объекта. Но это не так.

Когда вы используете метод setState, он ожидает обновления свойств. Но поскольку вы не предоставляете никаких свойств для setState, он ничего не сделает. Но он все равно будет повторно отображать ваш компонент по природе setState.

Обновление: по вашему запросу

Изменить: я также хочу указать (так как многие ответы относятся к тому факту, что я передаю пустой объект), что компонент повторно визуализируется (и обновляется), если я вызываю setState следующим образом:

this.setState({ results: this.state.results })

что похоже на это не должен вызывать повторный рендеринг.

React внутренне указывает, что он похож на this.state.results, когда вы пытаетесь обновить состояние с точно таким же состоянием. Значит, рендерить нечего. Но когда вы используете setState с пустым объектом, React отобразит sh его состояние, поэтому произойдет повторный рендеринг.

Прочтите это примечание из docs :

Примечание

Избегайте копирования реквизита в состояние! Это распространенная ошибка:

constructor(props) {
 super(props);
 // Don't do this!
 this.state = { color: props.color };
}

Проблема в том, что он не нужен (вы можете использовать this.props.color напрямую) и создает ошибки (обновления опоры цвета не будут отражаться в состоянии ).

0 голосов
/ 19 июня 2020

Когда вызывает this.setState, в основном React объединяет параметр, который мы передаем этой функции, со старым this.state, чтобы создать новое значение состояния. Например:

this.state = Object.assign({}, this.state, passedParam);

Таким образом, в приведенных выше фрагментах каждый раз, когда вы вызываете this.setState({});, создается новый объект состояния, что приводит к повторной визуализации компонента.

...