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/
547
Brad Parks 13 Июл 2014 в 05:11

5 ответов

Лучший ответ

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

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

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

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

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

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

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

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

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

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

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

555
Ankit 2 Дек 2019 в 16:45

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

5
lakmal_sathyajith 9 Окт 2018 в 10:56

Не все компоненты.

state в компоненте выглядит как источник водопада состояния всего приложения.

Таким образом, изменение происходит с того места, где вызывается setState. Затем дерево renders будет вызвано оттуда. Если вы использовали чистый компонент, render будет пропущен.

0
Singhi John 5 Дек 2019 в 03:50

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

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

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

102
Ankit 2 Дек 2019 в 15:35

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

  • реализовать 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>
6
Brad Parks 30 Янв 2019 в 12:14