it-roy-ru.com

ReactJS - Рендерится ли вызывается каждый раз, когда вызывается setState?

React перерисовывает все компоненты и подкомпоненты каждый раз, когда вызывается setState?

Если так, то почему? Я думал, что идея заключается в том, что React отображается только так мало, как нужно - когда состояние меняется.

В следующем простом примере оба класса воспроизводятся снова при нажатии на текст, несмотря на тот факт, что состояние не изменяется при последующих щелчках, поскольку обработчик onClick всегда устанавливает state в одно и то же значение:

this.setState({'test':'me'});

Я ожидал, что рендеринг будет происходить только в том случае, если изменились данные state.

Вот код примера, как JS Fiddle , и встроенный фрагмент:

var TimeInChild = React.createClass({
    render: function() {
        var t = new Date().getTime();

        return (
            <p>Time in child:{t}</p>
        );
    }
});

var Main = React.createClass({
    onTest: function() {
        this.setState({'test':'me'});
    },

    render: function() {
        var currentTime = new Date().getTime();

        return (
            <div onClick={this.onTest}>
            <p>Time in main:{currentTime}</p>
            <p>Click me to update time</p>
            <TimeInChild/>
            </div>
        );
    }
});

ReactDOM.render(<Main/>, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>
[1]: http://jsfiddle.net/fp2tncmb/2/
403
Brad Parks

React рендерит все компоненты и подкомпоненты каждый раз, когда вызывается setState?

По умолчанию - да.

Существует метод boolean shouldComponentUpdate (объект nextProps, объект nextState), у каждого компонента есть этот метод, и он отвечает за определение «должен ли компонент обновляться (запустить функцию render))?» каждый раз, когда вы изменяете состояние или передаете новый props из родительского компонента.

Вы можете написать собственную реализацию метода shouldComponentUpdate для своего компонента, но реализация по умолчанию всегда возвращает true - это означает, что функция рендеринга всегда перезапускается.

Цитата из официальных документов http://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate

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

Следующая часть вашего вопроса:

Если так, то почему? Я думал, что идея была в том, что React рендерится так мало, как нужно - когда состояние меняется. 

Есть два этапа того, что мы можем назвать «рендерингом»:

  1. Визуализация виртуального DOM: при вызове метода render возвращается новая структура virtual dom компонента. Как я упоминал ранее, этот метод render вызывается всегда, когда вы вызываете setState (), потому что shouldComponentUpdate всегда возвращает true по умолчанию. Таким образом, по умолчанию здесь нет оптимизации в React.

  2. Нативный рендеринг DOM: React изменяет реальные DOM-узлы в вашем браузере, только если они были изменены в Virtual DOM и настолько мало, насколько это необходимо - это отличная функция React, которая оптимизирует реальную мутацию DOM и делает React быстрой.

431
Petr

Нет, React не рендерит все, когда состояние меняется.

  • Всякий раз, когда компонент загрязнен (его состояние изменилось), этот компонент и его дочерние элементы повторно отображаются. Это, в некоторой степени, заключается в повторной визуализации как можно меньше. Единственный раз, когда рендер не вызывается, это когда какая-то ветка перемещается в другой корень, где теоретически нам не нужно ничего рендерить. В вашем примере TimeInChild является дочерним компонентом Main, поэтому он также перерисовывается при изменении состояния Main.

  • React не сравнивает данные о состоянии. Когда вызывается setState, он помечает компонент как грязный (что означает, что он должен быть повторно обработан). Важно отметить, что, хотя метод компонента render вызывается, реальный DOM обновляется только в том случае, если выходные данные отличаются от текущего дерева DOM (a.k.a различается между деревом Virtual DOM и деревом DOM документа). В вашем примере, хотя данные state не изменились, время последнего изменения изменилось, что сделало Virtual DOM отличным от DOM документа, поэтому HTML-код обновляется.

84
tungd

Да. Он вызывает метод render () каждый раз, когда мы вызываем setState, а когда «shouldComponentUpdate» возвращает false. 

3
lakmalsathyajith

Хотя это указано во многих других ответах здесь, компонент должен либо:

  • реализовать shouldComponentUpdate для рендеринга только при изменении состояния или свойств

  • переключиться на расширение PureComponent , который уже реализует метод shouldComponentUpdate для внутренних сравнений.

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

var TimeInChild = React.createClass({
    render: function() {
        var t = new Date().getTime();

        return (
            <p>Time in child:{t}</p>
        );
    }
});

var Main = React.createClass({
    onTest: function() {
        this.setState({'test':'me'});
    },

    shouldComponentUpdate: function(nextProps, nextState) {
      if (this.state == null)
        return true;
  
      if (this.state.test == nextState.test)
        return false;
        
      return true;
  },

    render: function() {
        var currentTime = new Date().getTime();

        return (
            <div onClick={this.onTest}>
            <p>Time in main:{currentTime}</p>
            <p>Click me to update time</p>
            <TimeInChild/>
            </div>
        );
    }
});

ReactDOM.render(<Main/>, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>

0
Brad Parks