Подробный жизненный цикл React (ниже): обновление (обновление)

внешний интерфейс GitHub API React.js

предисловие

Эта статья является ссылкой на книги с открытым исходным кодомReact In-depth: An exploration of UI developmentиндукция и усиление. В то же время, он также включает в себя часть моего собственного опыта в разработке.

Вы можете спросить, прочитав эту статью, полезно ли разрабатывать проекты, связанные с React, на работе? Честно говоря, это мало поможет. Эта статья не научит вас использовать новую технологию, она не поможет вам улучшить свои навыки программирования, но улучшит ваши знания React, такие как различение определенных концепций, понимание того, откуда берутся некоторые лучшие практики и т. д. Если вы настаиваете на том, чтобы рассматривать ценность этих знаний с утилитарной точки зрения, это будет очень полезно для вашего интервью.Точки знаний в этой статье часто спрашивают во время интервью, почему я знаю, потому что я съел их потерю .

Жизненный цикл компонентов React делится на рождение (монтирование), обновление (обновление) и смерть (размонтирование), но как узнать, на какой стадии находится компонент? Это можно узнать только через хуки-функции, предоставляемые нам компонентом React. Что такое хук-функция, так это функция, которая выполняется на определенном этапе. Например, конструктор будет вызываться только один раз на этапе рождения компонента, который считается «хуком». И наоборот, когда функция ловушки вызывается, это означает, что она вступила в определенный этап жизни, поэтому вы можете добавить некоторую логику кода в функцию ловушки для выполнения на определенной стадии. Конечно, это не абсолютно, например, функция рендеринга будет выполняться как на этапе рождения, так и на этапе обновления. Кстати, «хуки» также являются типом шаблона проектирования в программировании, например веб-хуки на github. Как следует из названия, это тоже хук, вы можете подписаться на события на github через Webhook, когда произойдет событие, github отправит POST-запрос на ваш сервис. С помощью этой функции вы можете прослушивать основную ветвь для нового события слияния, и если ваша служба получает сообщение об этом событии, вы можете, например, выполнить работу по развертыванию.

Мы объясняем каждую функцию крючка в хронологическом порядке этапов.

Пожалуйста, обратитесь к предыдущей статье для этапов рождения«Подробный жизненный цикл React (Часть 1): Стадия рождения (крепление)»

фаза обновления

  • componentWillReceiveProps()
  • shouldComponentUpdate()
  • componentWillUpdate()
  • render()
  • componentDidUpdate()

Фаза обновления запускается в трех случаях:

  • Изменятьprops: компонент не изменяет активно то, чем он владеетpropsсвойства, егоpropsСвойства передаются ему его родительским компонентом. Силовая параpropsВыполнение переназначения приведет к тому, что программа сообщит об ошибке.

  • Изменятьstate:stateизменения вносятсяsetStateреализован интерфейс. Синхронный дизайнstateОн требует навыков, какие состояния в него можно помещать, а какие нельзя, какие компоненты могут иметьstate, которые не разрешены; все они должны следовать определенным принципам. Есть шанс, что эту тему можно будет поднять самостоятельно

  • передачаforceUpdateМетод: мы упоминали об этом на предыдущем этапе, заставляя компонент обновляться.

setStateасинхронный

Большая часть причины обновления компонента связана с вызовомsetStateОбновление интерфейсаstateВ результате мы часто вызываем синхронноsetState,Но по фактуsetStateМетод асинхронный. Например следующий код:

onClick() {
  this.setState({
    count: 1,
  });
  console.log(this.state.count)
}

В обработчике события щелчка компонента мы обновляемstateсерединаcount, то сразу попробуйте прочитать последнююcount. Дело в том, что то, что вы читаете, не1, два должно быть предыдущим значением.

Более фатальной ошибкой является последовательный вызов одного и того же уровня блока, как здесь.setStateкод

this.setState({ ...this.state, foo: 42 });
this.setState({ ...this.state, isBar: true });

В этом случае первый наборfooЗначение будет перезаписано второй настройкой и восстановлено.

componentWillReceiveProps(nextProps)

при передаче компонентуpropsКогда происходит изменение, компонентcomponentWillReceivePropsТо есть вызов будет инициирован, а параметры, переданные методом, будут после внесения изменений.propsзначение (обычно мы называемnextProps). В этом методе вы можете пройтиthis.propsЧтобы получить доступ к текущему значению свойства, вы можете передатьnextPropsПолучите доступ к значениям свойств, которые должны быть обновлены, или сравните их, или вычислите их, чтобы определить состояние, которое вам нужно обновить (state) и, наконец, вызовsetStateМетоды обновления состояния. В этом вызове функции крюкаsetStateметод не запускает другой рендеринг.

Очень интересно, что хотяpropsизменения вызовутcomponentWillReceivePropsзвонок; ноcomponentWillReceivePropsЗвонок не означаетpropsДействительно изменился. Это не то, что я сказал, официальный представитель Facebook потратил целую статью, говоря об этом:(A => B) !=> (B => A). Например, посмотрите на следующий компонент:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      number: 1,
    }
    this.onClick = this.onClick.bind(this);
  }
  onClick() {
    this.setState({
      number: 1,
    })
  }
  render() {
    return (
      <MyButton onClick={this.onClick} data-number={this.state.number} />
    );
  }
}

Каждое событие клика используется повторноsetStateинтерфейсная параstateсделать обновление, но значение каждого обновления одинаково, т.е.number:1. И передайте состояние текущего компонента как свойство<MyButton />. Вот проблема, то каждый раз, когда я нажимаю кнопку, кнопкаMyButtonизcomponentWillReceivePropsбудут называть?

Да, даже если значение одинаково при каждом обновлении.

Причина этого на самом деле очень проста, потому что React не знает, изменилось ли переданное свойство. И почему React не пытается выполнить проверку на равенство?

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

Так что React раскрывает это изменение через функцию ловушки, не думайте, что когдаcomponentWillReceivePropsназываться значитpropsПроизошло изменение, и если вам нужно что-то сделать с этим изменением, обязательно выполните сравнение вручную.

shouldComponentUpdate()

shouldComponentUpdateВажно, что он может решить, продолжать ли текущий жизненный цикл. По умолчанию функция возвращаетtrueТо есть продолжить текущий жизненный цикл, также можно вернутьсяfalseЗавершает текущий жизненный цикл, предотвращая дальнейшееrenderсо следующими шагами.

Как мы только что сказали выше, React неpropsДля более глубокого сравнения эта параstateТо же самое применимо. так что даже еслиpropsа такжеstateне изменился,shouldComponentUpdateтакже будет вызван снова, включая следующие шагиcomponentWillUpdate,render,componentDidUpdateтакже будет работать снова. Очевидно, что это сильно снижает производительность.

Перейти кshouldComponentUpdateПараметры включают предстоящие измененияpropsа такжеstate, имя параметраnextPropsа такжеnextState, в этой функции вы также можете передатьthisключевое слово для доступа к текущемуstateа такжеprops, так что тут вы "всезнайка" и можете судить о том полностью или нет исходя из собственной бизнес логикиstateа такжеpropsпроизошло ли изменение, и решить, следует ли продолжать следующие шаги.shouldComponentUpdateОбычно это наш первый шаг при оптимизации производительности React. Оптимизация этого шага не только оптимизирует процесс самого компонента, но и экономит затраты на повторный рендеринг подкомпонентов.

Конечно, если судитьpropsЕсли логика обнаружения того, произошло ли изменение, относительно проста, например, это только поверхностное суждение (то есть, изменилась ли ссылка на объект), изменился ли объект, тогда вы можете использоватьPureRenderMixin:

import PureRenderMixin from 'react-addons-pure-render-mixin'; // ES6
const createReactClass = require('create-react-class');

createReactClass({
  mixins: [PureRenderMixin],

  render: function() {
    return <div className={this.props.className}>foo</div>;
  }
});

mininsЭто механизм, поддерживаемый React, который позволяет нескольким компонентам совместно использовать код.PureRenderMixinРабота плагина очень проста, он переписывает ее за васshouldComponentUpdateфункцию и неглубокое сравнение объектов, конкретный код можно найти изздесьа такжездесьоказаться.

В ES6 вы также можете использовать прямое наследованиеReact.PureComponentвместоReact.Componentдля достижения этой функции. По официальным словам React, это

React.PureComponent is exactly like React.Component, but implements shouldComponentUpdate() with a shallow prop and state comparison.

Pure

Подчеркнем еще раз,PureComponentВсе, что реализовано за вас, это определить, изменилась ли ссылка, и даже можно сказать, что она просто используется===суждения сделаны, так что это то, что мы называемpureпричина. Чтобы проиллюстрировать проблему конкретно, давайте возьмем практический пример.

/* MyButton.js: */
import React from 'react';

class MyButton extends React.PureComponent {
  constructor(props) {
    super(props);
  }
  render() {
    console.log('render');
    return <button onClick={this.props.onClick}>My Button</button>
  }
}
export default MyButton;

/* App.js: */
import React from 'react';
import MyButton from './Button.js';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      arr: [1],
    }
    this.onClick = this.onClick.bind(this);
  }
  onClick() {
    this.setState({
      arr: [...this.state.arr, 2],
    });
  }
  render() {
    return (
      <MyButton onClick={this.onClick} data-arr={this.state.arr} />
    );
  }
}

export default App;

В приведенном выше примере каждый щелчок будет изменятьstateсерединаarrПеременная,arrИ ссылка, и значение переменной изменились. Дело в томMyButtonКомпоненты наследуются отReact.PureComponent. Затем каждый раз, когда вы нажимаете,MyButtonИнформация о журнале будет распечатана, то есть каждый раз будет начинаться заново.render

если мы положимonClickМетод с некоторыми изменениями:

onClick() {
  const arr = this.state.arr;
  arr.push(2);
  this.setState({
    arr: arr,
  })
}

Этот метод также делаетarrПеременная изменилась, но только значение, а не ссылка, и при повторном нажатии кнопки (MyButton)Время,MyButtonне будет рендериться снова. то естьPureComponentНеглубокое сравнение было сделано для нас заранее.

Использование неизменяемых данных, которые изменяют только ссылки и не изменяют содержимое данных, часто используется как одно из средств оптимизации React.immutable.jsЭто может быть достигнуто для нас.Каждый раз, когда вы изменяете данные, вы фактически получаете новую ссылку на данные без изменения исходных данных. При этом эффект, которого хочет добиться редьюсер в Redux, на самом деле похож.reducerКлючевым моментом является его чистота (pure), которая не вызовет побочных эффектов при выполнении, то есть позволит избежать модификации входящих ссылок на данные, а также облегчит сравнение обновлений состояния компонентов.

componentWillUpdate()

componentWillUpdateМетоды иcomponentWillMountМетоды очень похожи, все они запускаются непосредственно перед рендерингом, здесь вы можете получитьnextPropsа такжеnextState, а также может получить доступ к текущему истекающемуpropsа такжеstate. Вы можете временно сохранить их для последующего использования, если это необходимо.

а такжеcomponentWillMountОтличие в том, что в этом методе нельзя использоватьsetState, в противном случае он немедленно вызовет еще один раунд рендеринга и повторный вызовcomponentWillUpdate, в бесконечном цикле.

componentDidUpdate()

Аналогично этапу монтирования, когда компонент входитcomponentDidUpdateСтадия означает, что последняя нативная DOM была визуализирована и доступна черезrefsполучить доступ. Эта функция будет передавать два параметра, которыеprevPropsа такжеprevState, как следует из названия, является предыдущим состоянием. ты еще можешь пройтиthisКлючевое слово обращается к текущему состоянию, потому что оно может обращаться к отношениям родной модели DOM, а также подходит для некоторых сторонних операций, которым необходимо манипулировать библиотекой классов.

Последовательность вызова каждой функции ловушки на этапе обновления также аналогична последовательности вызова на этапе монтирования, особенноcomponentDidUpdate, функция ловушки дочернего компонента вызывается перед родительским компонентом

Поскольку мы можем получить доступ к DOM, нам может понадобиться получить фактический стиль элемента в этой функции ловушки и написатьstateВ этом, например, ваш код может расти следующим образом:

componentDidUpdate(prevProps, prevState) {
// BAD: DO NOT DO THIS!!!
  let height = ReactDOM.findDOMNode(this).offsetHeight;
  this.setState({ internalHeight: height });
}

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

componentDidUpdate(prevProps, prevState) {
  // One possible fix...
  let height = ReactDOM.findDOMNode(this).offsetHeight;
  if (this.state.height !== height ) {
    this.setState({ internalHeight: height });
  }
}

стадия смерти

componentWillUnmount()

Эта функция ловушки срабатывает, когда компонент необходимо удалить из DOM. Здесь не так много нужно знать, в этой функции обычно выполняется некоторая работа, связанная с «очисткой».

  1. Отменить все отправленные сетевые запросы
  2. Удалите прослушиватель событий из DOM компонента.

Суммировать

Наконец, еще раз подчеркивается, что эта статья является книгой с открытым исходным кодом.React In-depth: An exploration of UI developmentиндукция. В принципе, достаточно прочитать эту книгу, если вы хотите понять жизненный цикл, и после прочтения вы непобедимы. Надеюсь, эта упрощенная китайская версия также будет вам полезна.

Эта статья также была опубликована в моемЗнай колонку, приветствую всех, чтобы обратить внимание

Ссылаться на