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

358

Реагирует ли повторно все компоненты и подкомпоненты каждый раз при вызове 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]: http://jsfiddle.net/fp2tncmb/2/
  • 0
    У меня была такая же проблема, я не знаю точное решение. Но я очистил ненужные коды от компонента, который начал работать как обычно.
  • 0
    Я хотел бы отметить, что в вашем примере - потому что то, как спроектирован ваш элемент, не зависит исключительно от уникального состояния - вызов setState() даже с фиктивными данными заставляет элемент отображаться по-другому, поэтому я бы сказал, да. Абсолютно следует попытаться перерисовать ваш объект, когда что-то могло измениться, потому что в противном случае ваша демонстрация - при условии, что это было предполагаемое поведение - не сработает!
Показать ещё 2 комментария
Теги:

4 ответа

444
Лучший ответ

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 быстрой.

  • 32
    Отличное объяснение! И единственное, что действительно заставляет React по-прежнему сиять в этом случае, это то, что он не меняет реальный DOM, если не был изменен виртуальный DOM. Так что, если моя функция рендеринга возвращает одно и то же 2 раза подряд, реальный DOM не изменяется вообще ... Спасибо!
  • 0
    Есть ли заменаState более эффективным?
Показать ещё 7 комментариев
83

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

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

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

  • 0
    Да, это правильный ответ. В качестве эксперимента измените последнюю строку как React.renderComponent (<div> <Main /> <TimeInChild /> </ div>, document.body) ; и удалите <TimeInChild /> из тела render () компонента Main . Функция render () TimeInChild не будет вызываться по умолчанию, поскольку она больше не является дочерней по отношению к Main .
  • 0
    Спасибо, что-то вроде этого немного сложно, поэтому авторы React рекомендовали, чтобы метод render() был «чистым» - независимым от внешнего состояния.
Показать ещё 4 комментария
1

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

  • 2
    Пожалуйста, будьте более сложными
0

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

  • реализовать 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>

Ещё вопросы

Сообщество Overcoder
Наверх
Меню