ReactJS - Вызывается ли визуализатор в любое время, когда вызывается "setState"?

Реагирует ли повторно все компоненты и подкомпоненты каждый раз при вызове setState?

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

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

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

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

Здесь приведен код примера как скрипт JS и встроенный фрагмент:

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

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

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

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

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

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

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

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

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

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

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

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

Ответ 2

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

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

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

Ответ 3

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

Ответ 4

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

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

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

Вот пример, использующий простой 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 == "me")
        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>