ReactJS - Чи викликається візуалізація будь-коли, коли викликається "setState"?


503

Чи 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/

У мене було те саме питання, я не знаю точного рішення. Але я очистив небажані коди з компонента, який почав працювати, як завжди.
Джейсон

Я хотів би зазначити, що у вашому прикладі - адже те, як створений ваш елемент, не покладається виключно на унікальний стан - виклик setState()навіть з фіктивними даними призводить до того, що елемент відображатиметься інакше, тому я б сказав "так". Абсолютно слід спробувати відновити об’єкт, коли щось могло змінитися, бо в іншому випадку ваша демонстрація - якщо припустити, що це призначена поведінка - не буде працювати!
Tadhg McDonald-Jensen

Ви можете мати рацію @ TadhgMcDonald-Jensen - але, наскільки я розумію, React мав би це зробити вперше (оскільки стан змінюється з нічого на щось, що перший раз), то ніколи не довелося повторювати. Я, звичайно, помилявся - тому що, схоже, React вимагає, щоб ви написали свій власний shouldComponentUpdateметод, який, як я вважав, проста версія його вже повинна бути включена в саму React. Здається, що версія за замовчуванням, що міститься в react, просто повертається true- що змушує компонент повторно відтворювати кожен раз.
Бред Паркс

Так, але його потрібно повторно відобразити у віртуальному DOM, тоді він змінює фактичний DOM лише в тому випадку, коли компонент надається інакше. Оновлення віртуальної DOM, як правило, незначні (принаймні порівняно із зміною речей на фактичному екрані), тому виклик відображається щоразу, коли це може знадобитися оновлення, то, бачачи, що жодна зміна не відбулася не дуже дорого і безпечніше, ніж припускати, що вона повинна зробити те саме.
Tadhg McDonald-Jensen

Відповіді:


570

Чи реагує React всі компоненти та підкомпоненти щоразу, коли викликається setState?

За замовчуванням - так.

Існує метод boolean shouldComponentUpdate (об’єкт nextProps, object nextState) , кожен компонент має цей метод, і він повинен визначити "чи слід оновити компонент (запустити функцію візуалізації )?" щоразу, коли ви змінюєте стан або передаєте нові реквізити від батьківського компонента.

Ви можете написати власну реалізацію методу shouldComponentUpdate для свого компонента, але реалізація за замовчуванням завжди повертає справжнє значення, тобто завжди перезапускати функцію візуалізації.

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

За замовчуванням, shouldComponentUpdate завжди повертає true, щоб запобігти непомітним помилкам, коли стан мутується на місці, але якщо ви обережні завжди ставитесь до стану як до непорушного та для читання лише з реквізиту та стану в рендері (), ви можете переосмислити функцію mustComponentUpdate з реалізація, яка порівнює старі реквізити та стан їх заміни.

Наступна частина вашого запитання:

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

Є два кроки того, що ми можемо назвати "візуалізацією":

  1. Віртуальна DOM візуалізації: коли метод render називається, він повертає нову віртуальну структуру dom компонента. Як я вже говорив раніше, це роблять метод викликається завжди при виклику SetState () , тому що shouldComponentUpdate завжди повертає істину за замовчуванням. Отже, за замовчуванням тут в Реакті немає оптимізації.

  2. Native DOM надає: React змінює реальні вузли DOM у вашому браузері лише у тому випадку, якщо вони були змінені у Virtual DOM та в міру необхідності - це та велика функція React, яка оптимізує реальну мутацію DOM та робить швидку реакцію.


47
Чудове пояснення! І одне, що дійсно змушує React все ще світити в цьому випадку, це те, що він не змінює реального DOM, якщо віртуальний DOM не був змінений. Отже, якщо моя функція візуалізації повертає те саме, що два рази поспіль, справжній DOM взагалі не змінюється ... Дякую!
Бред Паркс

1
Я думаю, що @tungd має рацію - дивіться його відповідь нижче. Це також питання відносин батько-дитина між компонентами. Наприклад, якщо TimeInChild є рідним братом Main, то його рендеринг () не буде викликатися зайвим.
gvlax

2
Якщо ваш стан містить відносно великий об’єкт javascript (~ 1k загальних властивостей), який відображається у вигляді великого дерева компонентів (~ 100 всього) ... чи дозволите ви, щоб функції візуалізації побудували віртуальний будинок, або ви повинні перед встановленням стан, порівняйте новий стан зі старим вручну і дзвоніть лише, setStateякщо виявите, що є різниця? Якщо так, то як це зробити найкраще - порівняйте рядки json, побудуйте та порівняйте хеші об’єктів, ...?
Vincent Sels

1
@Petr, але навіть якщо реагувати реконструювати віртуальний dom, якщо старий віртуальний dom такий же, як новий віртуальний dom, dom браузера не буде торкатися, чи не так?
Джессі

3
Також розгляньте використання React.PureComponent ( reactjs.org/docs/react-api.html#reactpurecomponent ). Він оновлюється (відтворюється) лише тоді, коли стан компонента або реквізит фактично змінилися. Однак остерігайтеся, щоб порівняння було неглибоким.
дебат

105

Ні, React не робить все, коли стан змінюється.

  • Кожен раз, коли компонент забруднений (його стан змінився), цей компонент та його діти повторно передаються. Це певною мірою полягає в тому, щоб якомога менше рендерингувати. Єдиний час, коли візуалізація не викликається, це коли певна гілка переміщена до іншого кореня, де теоретично нам не потрібно нічого рендерингувати. У вашому прикладі TimeInChildє дочірньою складовою Main, тому вона також отримує повторне відображення, коли стан Mainзмінюється.

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


Так, це правильна відповідь. Як експеримент змініть останній рядок як React.renderComponent (<div> <Main /> <TimeInChild /> </div>, document.body) ; і видалити <TimeInChild /> з тіла візуалізації () з головного компонента. Візуалізації () з TimeInChild не викликатиме за замовчуванням , тому що це не дитина Main більше.
gvlax

Дякую, подібні речі трохи хитрі, тому автори React рекомендували render()метод бути "чистим" - незалежним від зовнішнього стану.
tungd

3
@tungd, що це означає some branch is moved to another root? Що ти називаєш branch? Що ти називаєш root?
Зелений

2
Я дуже вважаю за краще відповідь, позначена як правильна. Я розумію вашу інтерпретацію "рендерінгу" на рідній, "реальній" стороні ... але якщо ви подивитесь на це з боку, що реагує на реакцію, ви повинні сказати, що вона повторюється знову. На щастя, досить розумно визначити, що насправді змінилося, і лише оновити ці речі. Ця відповідь може бути бентежною для нових користувачів, спочатку ви скажете «ні», а потім поясните, що все буде винесено ...
WiRa

1
@tungd Ви можете, будь ласка, пояснити питання Грінаwhat does it mean some branch is moved to another root? What do you call branch? What do you call root?
madhu131313

7

Хоча це вказано в багатьох інших відповідях тут, компонент повинен:

  • застосовувати 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

Так. Він викликає метод render () щоразу, коли ми викликаємо setState замість того, коли "shouldComponentUpdate" повертає значення false.


7
Будьте докладнішими
Картік Рамеш

3

Ще одна причина "втраченого оновлення" може бути наступною:

  • Якщо статичний getDerivedStateFromProps визначений, він повторюється в кожному процесі оновлення відповідно до офіційної документації https://reactjs.org/docs/react-component.html#updating .
  • тож якщо це значення стану надходить з реквізиту на початку, воно перезаписується в кожному оновленні.

Якщо це проблема, то U може уникнути встановлення стану під час оновлення, слід перевірити таке значення параметра стану, як це

static getDerivedStateFromProps(props: TimeCorrectionProps, state: TimeCorrectionState): TimeCorrectionState {
   return state ? state : {disable: false, timeCorrection: props.timeCorrection};
}

Іншим рішенням є додавання ініціалізованого властивості до стану та встановлення його в перший раз (якщо стан ініціалізовано до ненульового значення.)


0

Не всі компоненти

stateв компоненті виглядає як джерело водоспаду стан всієї APP.

Тож зміна відбувається звідки дзвонив setState. Дерево rendersтоді дзвонять звідти. Якщо ви використовували чистий компонент, renderзапит буде пропущено.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.