Функциональный setState — будущее React

внешний интерфейс Безопасность JavaScript React.js

Эта статья переведена с:Функциональный SetState - это будущее реагирования - FreeCodeCamp.org

Примечание переводчика: вчера я столкнулся с setState яма[Реагировать, наступив на яму] setState может пойти не так!Записано в этой статье, я зашел в Интернет в Google и увидел эту статью.О setState эта статья очень подробно объясняет, поэтому я перевожу ее на Nuggets, чтобы ее увидело больше людей.

Обновление: у меня есть продолжение обсуждения этой темы на React Rally. Хотя этот пост больше о шаблоне «функциональный setState», он больше посвящен глубокому пониманию setState.

Поделка, которую я сделал на setState на React RallyJustice Mba - Demystifying setState() - YouTube

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

Примечание переводчика: приведенный выше перевод выглядит следующим образом:

Экосистема JavaScript переходит от «нового фреймворка недели» к «новому (более быстрому) клону React недели».

Новости ReactJS @ReactJSNews
Alibaba выпустила собственный React-подобный фреймворк, который кажется легче и быстрее, но у него определенно есть недостаток!github.com/alibaba/rax

Но команда React далеко не расслабляется. Они продолжают копать глубже и исследовать более функциональные жемчужины.

Итак, сегодня я раскрываю вам функциональную жемчужину, скрытую в React —функциональный setState!

好吧,这个名字只是我刚刚编造的......而且这并不是全新的东西或秘密。 Нет, не совсем.其实它是React内置的一种模式,只有很少有开发人员知道这种模式。 它从来没有名字,但现在它可以叫做 -функциональный setState!

Функциональный setState является одним из таких шаблонов, описанных Дэном Абрамовым:

«Изменения состояния объявляются отдельно от класса компонента».

Хм?

Ну ... это то, что вы уже знаете

React — это библиотека пользовательского интерфейса на основе компонентов. Компонент — это, по сути, функция, которая принимает некоторые свойства и возвращает элемент пользовательского интерфейса.

function User(props) {
  return (
    <div>A pretty user</div>
  );
}

Компонентам может потребоваться владеть своим состоянием и управлять им. В этом случае вы обычно пишете компонент как класс. Тогда ваше состояние существует в классеconstructorВ функции:

class User {
  constructor () {
    this.state = {
      score : 0
    };
  }
  render () {
    return (
      <div>This user scored {this.state.score}</div>
    );
  }
}

Для управления состоянием React предоставляетsetState()специальный метод. Использование заключается в следующем:

class User {
  ... 
  increaseScore () {
    this.setState({score : this.state.score + 1});
  }
  ...
}

осторожностьsetState()Принцип работы. Вы передаете объект, содержащий часть состояния, для обновления. Другими словами, передаваемый объект будет иметь ключи, соответствующие ключам в состоянии компонента, а затемsetState()Обновите или установите состояние, объединив объекты в состояние. Это "установленное состояние"

ты можешь не знать

помнишь, что мы сказалиsetState()это работает? А что, если я скажу вам, что вместо объекта вы можете передать функцию?

да.setState()Также принимает функцию в качестве параметра. Эта функция принимает предыдущее состояние компонента и текущие реквизиты, которые она использует для вычисления и возврата следующего состояния. Следующим образом:

this.setState(function (state, props) {
 return {
  score: state.score - 1
 }
});

осторожность,setState()это функция, которой мы передаем другую функцию (функциональное программирование... функциональный setState). На первый взгляд код может выглядеть некрасиво, только шагов для установки состояния слишком много. Но почему вы должны это делать?

Почему вы передаете функцию в setState?

Дело в том,Обновления статуса могут быть асинхронными.

подумай о звонкеsetState()что просходит. React сначала перейдет кsetState()Объект сливается с текущим состоянием. Затем он начнет сливаться. Он создаст новое дерево элементов React (объектное представление пользовательского интерфейса), отличающее новое дерево от старого дерева в соответствии сsetState()Объект, чтобы узнать, что изменилось, а затем, наконец, обновит DOM.

вызов! Так много работы! На самом деле, это даже упрощенное изложение. Но доверяйте React:

React не просто «устанавливает состояние».

В связи с объемом работы звоню.setState()Ваш статус может быть обновлен не сразу.

React может объединять несколькоsetState()Вызовы объединяются в одно обновление для повышения производительности.

Что означает приведенная выше фраза?

Во-первых, «множественные вызовыsetState()"может означать несколько вызовов внутри функцииsetState(),Следующим образом:

state = {score : 0};
// 多次调用`setState()
increaseScoreBy3 () {
 this.setState({score : this.state.score + 1});
 this.setState({score : this.state.score + 1});
 this.setState({score : this.state.score + 1});
}

Теперь, когда React встречает «Несколько вызововsetState(), вместо того, чтобы выполнять «set-state» ровно три раза, React будет избегать большей части работы, описанной выше, и тонко скажет себе: «Нет! Я не планирую сворачивать горы и каждый раз обновлять какой-то статус. Я бы предпочел получить контейнер, который объединяет все эти фрагменты вместе и требует обновления только один раз. "Это пакетная обработка!

Не забудьте перейти кsetState()является обычным объектом. Теперь предположим, что каждый раз, когда React сталкивается с «множественными вызовамиsetState()", который передается каждому изsetState()вызовите все объекты для завершения пакета, объедините их вместе в один объект, а затем используйте этот единственный объект для выполненияsetState().

В JavaScript объединенный объект может выглядеть так:

const singleObject = Object.assign(
  {}, 
  objectFromSetState1, 
  objectFromSetState2, 
  objectFromSetState3
);

Этот шаблон называется композицией объектов.

В JavaScript объекты «объединяются» или комбинируются таким образом, что если три объекта имеют один и тот же ключ, значение ключа последнего объекта, переданного в Object.assign(), будет окончательным значением для этого ключа. Например:

const me  = {name : "Justice"}, 
      you = {name : "Your name"},
      we  = Object.assign({}, me, you);
we.name === "Your name"; //true
console.log(we); // {name : "Your name"}

потому чтоyouБыть объединеннымweпоследний объект , поэтомуyouв объектеnameЗначение - "Ваше имя" - переопределитmeв объектеnameценность .

Итак, если вы вызываете несколько раз с объектом в качестве параметраsetState()- Передавайте по одному объекту за раз - React сольется. Другими словами, он создаст новый объект из нескольких объектов, которые мы передаем. Если какой-либо объект содержит один и тот же ключ, сохраните значение ключа последнего объекта с тем же ключом. Верно?

Это означает, что с учетом нашего вышеincreaseScoreBy3функция, окончательный результат функции будет просто1вместо3, потому что React не позвонил нам сразуsetState()Статус последовательного обновления. Во-первых, React объединяет все объекты вместе, и результат выглядит следующим образом:{score:this.state.score + 1}, а затем только один раз "установить состояние" с вновь составленным объектом. так:User.setState({score:this.state.score + 1}.

Чтобы быть очень ясным, передайте объект вsetState()Не проблема. Настоящая проблема заключается в том, что вы хотите вычислить следующее состояние на основе предыдущего состояния, передав объект вsetState(). Так что прекрати это делать. Это небезопасно!

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

София Шумейкерэтот примерЭту проблему можно продемонстрировать. Продемонстрируйте это и обратите внимание на плохие и хорошие решения в этом примере.

Функциональный setState решил нашу проблему

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

Когда вы продемонстрируете приведенный выше пример, вы, несомненно, увидите, что функционал setState решает нашу проблему. Но как именно?

Давайте спросим одного из основных членов React — Дэна.

Твиттер Дэна

Обратите внимание на ответ, который он дал.

Когда вы используете функциональный setState...

Обновления будут помещены в очередь и выполнены в том порядке, в котором они вызываются.

Поэтому, когда React сталкивается с «множественными функциональнымиsetState()Вместо того, чтобы объединять объекты вместе, React ставит функции в очередь «в том порядке, в котором они были вызваны» (конечно, объекты для слияния отсутствуют).

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

Ниже мы издеваемся над методом setState() в будущем, чтобы дать вам представление о том, что делает React. Также для уменьшения многословия мы будем использовать ES6. Вы всегда можете написать версию ES5, если хотите.

Во-первых, давайте создадим класс компонента. Затем внутри него мы создадим поддельный метод setState(). Кроме того, наш компонент будет иметь метод увеличенияScoreBy3(), который будет выполнять многофункциональное setState. Наконец, мы создадим экземпляр класса, как это делает React.

class User{
  state = {score : 0};
  //let's fake setState
  setState(state, callback) {
    this.state = Object.assign({}, this.state, state);
    if (callback) callback();
  }
  // 多次函数式 setState 的调用
  increaseScoreBy3 () {
    this.setState( (state) => ({score : state.score + 1}) ),
    this.setState( (state) => ({score : state.score + 1}) ),
    this.setState( (state) => ({score : state.score + 1}) )
  }
}
const Justice = new User();

Обратите внимание, что setState также принимает необязательный второй параметр — функцию обратного вызова. Если этот параметр передан, React вызывает его после обновления состояния.

Теперь, когда пользователь запускаетincreaseScoreBy3(), React поставит в очередь несколько функциональных setState s. Мы не собираемся подделывать эту логику здесь, потому что наша точка зренияЧто действительно делает функциональный setState безопасным. Но вы можете думать о результате процесса «постановки в очередь» как о массиве функций, например:

const updateQueue = [
  (state) => ({score : state.score + 1}),
  (state) => ({score : state.score + 1}),
  (state) => ({score : state.score + 1})
];

Наконец, давайте смоделируем процесс обновления:

// 按顺序递归调用 state 的更新
function updateState(component, updateQueue) {
  if (updateQueue.length === 1) {
    return component.setState(updateQueue[0](component.state));
  }
return component.setState(
    updateQueue[0](component.state), 
    () =>
     updateState( component, updateQueue.slice(1)) 
  );
}
updateState(Justice, updateQueue);

Да, это не лучший код, вы определенно можете написать код лучше. Но ключевой момент здесь в том, что каждый раз, когда React выполняет функцию в функции setState, React будет обновлять ваше состояние, передавая ему новую копию обновленного состояния. Это позволяет функции setState устанавливать новое состояние на основе предыдущего состояния. Здесь я создал корзину с полным кодом.

Я завершил этот пример, чтобы вы могли лучше понять его.

class User{
  state = {score : 0};
  //fake setState
  setState(state, callback) {
    console.log("state", state);
    this.state = Object.assign({}, this.state, state);
    if (callback) callback();
  }
}

const Justice = new User();

const updateQueue = [
  (state) => ({score : state.score + 1}),
  (state) => ({score : state.score + 1}),
  (state) => ({score : state.score + 1})
];

// 按顺序递归调用 state 的更新
function updateState(component, updateQueue) {
  if (updateQueue.length === 1) {
    return component.setState(updateQueue[0](component.state));
  }

  return component.setState(
    updateQueue[0](component.state), 
    () =>
     updateState( component, updateQueue.slice(1)) 
  );
}

Запустите этот код, чтобы убедиться, что вы его понимаете. Когда вы вернетесь, мы увидим, что заставляет функциональный setState действительно сиять.

Я просто открою тебе секрет

До сих пор мы углублялись в то, почему безопасно использовать несколько функциональных setStates в React. Но на самом деле мы еще не закончили с полным определением функционального setState: «объявление изменений состояния отдельно от классов компонентов».

В течение многих лет логика состояния настройки, то есть функция или объект, которые мы передаем в setState(), всегда существовала в классе компонентов, что было скорее императивным, чем декларативным.

Что ж, сегодня я покажу вам недавно обнаруженное сокровище — лучшие секреты React:

адрес этого твита

Спасибо Дэну Абрамову!

В этом сила функционального setState. Объявите логику обновления состояния вне класса компонента. Затем вызовите его в классе компонента.

// outside your component class
function increaseScore (state, props) {
  return {score : state.score + 1}
}
class User{
  ...
// inside your component class
  handleIncreaseScore () {
    this.setState( increaseScore)
  }
  ...
}

Это декларативно! Ваш класс компонентов больше не заботится об обновлениях состояния. Он просто объявляет тип обновления, который он хочет.

Чтобы глубже понять это, рассмотрим те сложные компоненты, которые часто имеют множество срезов состояния, каждый из которых обновляется при различных операциях. Иногда для каждой функции обновления требуется несколько строк кода. Вся эта логика будет жить в вашем компоненте. Но не больше!

Кроме того, я предпочитаю, чтобы каждый модуль был как можно короче. Если вы чувствуете, как и я, что ваш текущий модуль слишком длинный, вы можете извлечь всю логику изменения состояния в какой-нибудь другой модуль, а затем импортировать и использовать его в своем компоненте.

import {increaseScore} from "../stateChanges";
class User{
  ...
  // inside your component class
  handleIncreaseScore () {
    this.setState( increaseScore)
  }
  ...
}

Теперь вы даже можете повторно использовать функцию увеличенияScore в другом компоненте, просто импортируйте ее.

Что еще вы можете сделать с функциональным setState?

Сделайте тестирование легким!

адрес этого твита

Вы также можете передать дополнительные параметры для расчета следующего состояния (это просто поражает...)

адрес этого твита

Ждем большего...

Будущее Реакта

В течение многих лет команда React изучала, как лучше всего реализовать функции с отслеживанием состояния. Функциональный setState кажется правильным ответом (вероятно).

Эй, Дэн! Последнее (еще одно) слово (чтобы посмотреть на React)?

адрес этого твита

Если вы видели это, вы, вероятно, так же взволнованы, как и я. Начните экспериментировать с функциональным setState уже сегодня!

Удачного кодирования!