Что такое подъем состояния вверх

Концепции React при работе с компонентами

В данном уроке мы изучим основные концепции React при работе с компонентами. В предыдущих уроках вы уже познакомились с ними на практике. Давайте теперь подведем под них теоретическую базу, а также изучим терминологию.

Данные компонентов

Данные компонентов могут хранится в трех местах: в пропсах, в переменных и константах компонента, и в стейтах. В пропсах хранятся данные, которые посылает компоненту извне его родительский компонент. При этом внутри компонента пропсы должны оставаться неизменными.

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

Дан некий компонент:

Определите, чем могут быть isEdit, elem и data: пропсом, стейтом, переменной, либо константой.

Контейнерные и презентационные компоненты

Компоненты в React рекомендовано разбивать на два типа: (умные, толстые) и (глупые, худые).

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

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

Преимущество такого подхода в легком переиспользовании презентационных компонентов, так как такие компоненты не знают, откуда берутся данные, а просто отображают их. Это значит, что один и тот же компонент мы можем использовать в разных ситуациях, передавая ему данные из разных источников.

Расскажите, в каких случаях презентационный компонент может содержать стейты. Встречались ли такие ситуации в предыдущих уроках?

Однонаправленный поток данных

Компонент может передавать свое состояние вниз по дереву потомков в виде пропсов дочерних компонентов. Этот процесс называется ( англ. top-down ) или ( англ. unidirectional ) потоком данных. Состояние всегда принадлежит определенному компоненту, и это состояние может влиять только на компоненты, являющиеся потомками данного компонента.

Подъем состояний

Часто несколько компонентов должны отражать одни и те же изменяющиеся данные. В React рекомендовано поднимать общее состояние до ближайшего общего предка. Давайте посмотрим на примере.

Пусть мы хотим сделать калькулятор температуры воды. Он будет представлять собой инпут, в который пользователь будет вводить температуру, и абзац, в который будет выводится вердикт: вода жидкая, вода твердая, вода газообразная.

Пусть наш калькулятор представляет собой компонент-контейнер Calculator :

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

В компоненте TempInp должен быть инпут для изменения температуры. Но, так как эта температура является пропсом данного компонента, то непосредственно в TempInp изменять его нельзя. Правильно будет передать из компонента Calculator специальную функцию для изменения температуры:

Можно, кстати, не вводить новую функцию, а передать в дочерний компонент функцию setTemp :

Придумайте 3 задачи, в которых нужно использовать подъем состояния. Напишите решения этих задач.

Один источник истины

Напишите реализацию описанной задачи.

Некий программист написал код, выводящий сумму элементов массива:

Что не так с этим кодом? Исправьте его.

Некий программист написал код для редактирования элементов массива:

Источник

Подъём состояния

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

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

Кроме того, он рендерит BoilingVerdict для текущего значения поля ввода.

Добавление второго поля ввода

Добавим к полю ввода градусов Цельсия поле ввода по шкале Фаренгейта. Оба поля будут синхронизироваться.

Теперь можем изменить Calculator для рендера двух отдельных полей ввода температуры:

Сейчас у нас есть два поля ввода, но когда вы вводите температуру в одно из них, другое поле не обновляется. Это противоречит нашему требованию — мы хотим их синхронизировать.

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

Во-первых, мы напишем две функции для конвертации градусов по шкале Цельсия в Фаренгейт и обратно:

Эти две функции конвертируют числа. Мы напишем ещё одну функцию, которая принимает строку с температурой ( temperature ) и функцию конвертации ( convert ) в качестве аргументов, и возвращает строку. Мы будем использовать эту функцию для вычисления значения одного поля ввода на основе значения из другого поля ввода.

Данная функция возвращает пустую строку при некорректном значении аргумента temperature и округляет возвращаемое значение до трёх чисел после запятой:

В настоящее время оба компонента TemperatureInput независимо хранят свои значения каждое в собственном локальном состоянии:

Однако мы хотим, чтобы эти два поля ввода синхронизировались друг с другом. Когда мы обновляем поле ввода градусов по Цельсию, поле ввода градусов по Фаренгейту должно отражать сконвертированную температуру и наоборот.

Давайте шаг за шагом посмотрим, как это работает.

Мы знаем, что пропсы доступны только для чтения. Когда temperature находилась во внутреннем состоянии, TemperatureInput мог просто вызвать this.setState() для изменения его значения. Однако теперь, когда temperature приходит из родительского компонента в качестве пропа, TemperatureInput не может контролировать его.

Теперь, когда TemperatureInput хочет обновить свою температуру, он вызывает this.props.onTemperatureChange :

Мы будем хранить текущие значения temperature и scale во внутреннем состоянии этого компонента. Это состояние, которое мы «подняли» от полей ввода, и теперь оно будет служить «источником истины» для них обоих. Это минимальное представление всех данных, про которое нам нужно знать для рендера обоих полей ввода.

Например, если мы вводим 37 как значение поля ввода для температуры по шкале Цельсия, состояние компонента Calculator будет:

Если позднее мы изменим поле для ввода градусов по шкале Фаренгейта на 212, состояние Calculator будет:

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

Теперь, независимо от того, какое поле ввода вы редактируете, this.state.temperature и this.state.scale в Calculator обновляются. Одно из полей ввода получает значение как есть, поэтому введённые пользователем данные сохраняются, а значение другого поля ввода всегда пересчитывается на их основе.

Давайте посмотрим, что происходит, когда вы редактируете поле ввода:

Каждое обновление проходит через одни и те же шаги, поэтому поля ввода остаются синхронизированными.

Для любых изменяемых данных в React-приложении должен быть один «источник истины». Обычно состояние сначала добавляется к компоненту, которому оно требуется для рендера. Затем, если другие компоненты также нуждаются в нём, вы можете поднять его до ближайшего общего предка. Вместо того, чтобы пытаться синхронизировать состояние между различными компонентами, вы должны полагаться на однонаправленный поток данных.

Для подъёма состояния приходится писать больше «шаблонного» кода, чем при подходах с двусторонней привязкой данных, но мы получаем преимущество в виде меньших затрат на поиск и изолирование багов. Так как любое состояние «живёт» в каком-нибудь компоненте, и только этот компонент может его изменить, количество мест с возможными багами значительно уменьшается. Кроме того, вы можете реализовать любую пользовательскую логику для отклонения или преобразования данных, введённых пользователем.

Когда вы видите, что в UI что-то отображается неправильно, то можете воспользоваться расширением React Developer Tools. С помощью него можно проверить пропсы и перемещаться по дереву компонентов вверх до тех пор, пока не найдёте тот компонент, который отвечает за обновление состояния. Это позволяет отследить источник багов:

Источник

Русские Блоги

9. Поднятие состояния вверх

Версия React: 15.4.2
** Перевод: xiyoki **

Обычно несколько компонентов должны реагировать на одни и те же изменения данных. Мы рекомендуем повысить общее состояние до ближайшего общего предка. Посмотрим, как это работает.
В этом разделе мы создадим калькулятор температуры, чтобы вычислить, кипит ли вода при данной температуре.
Нас назовут BoilingVerdict Компоненты запускаются. Он принимает температуру по Цельсию в качестве реквизита и печатает, достаточно ли ее вскипятить:

Далее мы создадим файл с именем Calculator компонент s. Это сделает Позволяет ввести температуру и сохранить значение в this.state.value в.
Кроме того, он отображает текущие значения BoilingVerdict 。

Добавление второго входа (добавление второго входа)

Наше новое требование: помимо поля ввода Цельсия, мы также предоставляем поле ввода по Фаренгейту, и они синхронизируются.
берем из Calculator Извлечь один TemperatureInput Компоненты для начала. Мы добавим к нему новую опору масштаба, и эту опору можно будет ‘c’ так же может быть ‘f’ 。

Теперь мы можем изменить Calculator Чтобы отобразить два независимых входа температуры:

Теперь у нас есть два поля ввода, но когда вы вводите температуру в одно из полей ввода, другое не будет обновляться. Это нарушает наши требования: мы хотим, чтобы они оставались синхронизированными.
мы не можем Calculator Показать в BoilingVerdict 。 Calculator Не знаю текущую температуру, потому что текущая температура скрыта TemperatureInput внутренний.

Подъем состояния вверх

Во-первых, мы напишем две функции для преобразования Цельсия в Фаренгейт и Фаренгейта в Цельсий:

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

Например, tryConvert(‘abc’,toCelsius) Возвращает пустую строку, tryConvert(’10.22’,toFahrenheit) возвращение ‘50.396’ 。
Далее мы начнем TemperatureInput Удалить статус.
Вместо этого TemperatureInput Компонент примет value с участием onChange Обработчик как опора:

Независимо от того, какое поле ввода вы редактируете, Calculator средний this.state.value с участием this.state.scale Будет обновлено. Значение, полученное в одном из полей ввода, остается неизменным, поэтому любой вводимый пользователем ввод сохраняется, а значение в другом поле ввода всегда пересчитывается на его основе.

Извлеченные уроки (Извлеченные уроки)

Источник

Часто несколько компонентов должны отражать одни и те же изменения данных. Мы рекомендуем поднимать общее состояние до ближайшего общего предка. Давайте посмотрим, как это работает в действии.

В этом разделе мы создадим калькулятор температуры, который вычисляет, будет ли вода кипеть при данной температуре. Начнем с компонента, который называется BoilingVerdict. Он принимает celsius температуру в качестве опоры и печатает, достаточно ли кипятить воду:

Затем мы создадим компонент, который называется Calculator. Это дает возможность ввести температуру и сохранить ее значение this.state.temperature. Кроме того, он отображает BoilingVerdict текущее значение ввода.

Добавление второго входа

Наше новое требование состоит в том, что в дополнение к входу Celsius мы предоставляем вход по Фаренгейту, и они синхронизируются. Мы можем начать с извлечения TemperatureInputкомпонента из Calculator. Мы добавим к нему новую scaleопору, которая может быть «c»или «f»:

Теперь мы можем изменить, Calculator чтобы сделать два отдельных входа температуры:

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

Мы также не можем отобразить BoilingVerdict с Calculator. Он Calculator не знает текущую температуру, потому что он скрыт внутри TemperatureInput.

Написание функций преобразования

Во-первых, мы напишем две функции для преобразования от Цельсия к Фаренгейту и обратно:

Эти две функции преобразуют числа. Мы напишем еще одну функцию, которая принимает строку temperature и функцию преобразователя в качестве аргументов и возвращает строку. Мы будем использовать его для вычисления значения одного входа на основе другого ввода.

Он возвращает пустую строку с недопустимым значением temperature и сохраняет округление до третьего десятичного знака:

Например, tryConvert(‘abc’, toCelsius) возвращает пустую строку и tryConvert(‘10.22’, toFahrenheit) возвращает ‘50.396’.

Подъемное состояние вверх (Lifting State Up)

В настоящее время оба TemperatureInput компонент независимо сохраняет свои значения в локальном состоянии:

Однако мы хотим, чтобы эти два входа синхронизировались друг с другом. Когда мы обновляем вход Celsius, вход Fahrenheit должен отражать преобразованную температуру и наоборот.

В React состояние совместного доступа достигается путем перемещения его до ближайшего общего предка компонентов, которые в нем нуждаются. Это называется «подъем состояния вверх». Мы удалим локальное состояние из TemperatureInputи переместим его в Calculatorвместо.

Если им Calculator принадлежит общее состояние, оно становится «источником истины» для текущей температуры на обоих входах. Он может поручить им обе иметь значения, которые согласуются друг с другом. Поскольку реквизиты обоих TemperatureInput компонентов поступают из одного и того же родительского Calculator компонента, два входа всегда будут синхронизироваться.

Давайте посмотрим, как это работает шаг за шагом.

Во-первых, мы заменим this.state.temperatureс this.props.temperature в TemperatureInput компоненте. Пока давайте притворимся, что this.props.temperature уже существует, хотя нам нужно будет передать его из Calculator будущего:

Мы знаем, что реквизит доступен только для чтения. Когда он temperature был в локальном состоянии, он TemperatureInput мог просто позвонить, this.setState() чтобы изменить его. Однако теперь, когда temperatureон исходит от родителя в качестве опоры, TemperatureInput он не контролирует его.

В React это обычно решается путем создания компонента «контролируемого». Точно так же, как DOM принимает как a, так value и onChangeопору, так что пользователь может TemperatureInput принять оба temperature и onTemperatureChange реквизит от своего родителя Calculator.

Теперь, когда TemperatureInput требуется обновить его температуру, он вызывает this.props.onTemperatureChange:

onTemperatureChange будет обеспечена вместе с temperature опорой с помощью родительского Calculator компонента. Он будет обрабатывать изменение, изменяя его собственное локальное состояние, тем самым повторно отображая оба входа с новыми значениями. Мы Calculator очень скоро посмотрим на новую реализацию.

Перед тем, как погрузиться в изменения Calculator, давайте вспомним наши изменения в TemperatureInput компоненте. Мы удалили из него локальное состояние, и вместо чтения this.state.temperature мы теперь читаем this.props.temperature. Вместо того, чтобы звонить, this.setState() когда мы хотим внести изменения, мы теперь вызываем this.props.onTemperatureChange(), которые будут предоставлены Calculator:

Теперь перейдем к Calculator компоненту.

Мы будем хранить текущие входные данные temperature и scale в их локальном состоянии. Это состояние, которое мы «подняли» от вкладов, и оно будет служить «источником истины» для обоих из них. Это минимальное представление всех данных, которые нам нужно знать, чтобы отображать оба входа.

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

Если позднее мы изменим поле Фаренгейта на 212, состояние Calculator будет:

Мы могли бы сохранить значение обоих входов, но это оказалось ненужным. Достаточно сохранить значение последнего измененного ввода и масштаб, который он представляет. Затем мы можем вывести значение другого входа на основе текущего temperature и scale одного.

Входы остаются в синхронизации, поскольку их значения вычисляются из одного и того же состояния:

Теперь, независимо от того, какой ввод вы редактируете, this.state.temperature и this.state.scale в Calculator обновлении. Один из входов получает значение как есть, поэтому любой пользовательский ввод сохраняется, а другое входное значение всегда пересчитывается на его основе.

Давайте вспомним, что происходит, когда вы редактируете ввод:

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

Подъемное состояние включает в себя написание более «шаблонного» кода, чем подходы с двусторонней привязкой, но в качестве выгоды требуется меньше усилий для поиска и изоляции ошибок. Так как любое состояние «живет» в некотором компоненте, и только этот компонент может его изменить, площадь поверхности для ошибок значительно уменьшается. Кроме того, вы можете реализовать любую пользовательскую логику для отклонения или преобразования пользовательского ввода.

Если что-то может быть получено либо из реквизита, либо из состояния, оно, вероятно, не должно находиться в состоянии. Например, вместо сохранения обоих celsiusValue и fahrenheitValue, мы сохраняем только последний отредактированный temperature и его scale. Значение другого входа всегда может рассчитываться из них в render() методе. Это позволяет нам очистить или применить округление к другому полю, не теряя при этом точности ввода.

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

Источник

2.11 Подъём состояния выше по иерархии

Очень часто несколько компонентов должны отражать одни и те же данные, которые меняются с течением времени. В таких случаях следует поднимать состояние выше по иерархии: к их ближайшему общему предку. Давайте сделаем это в конкретном примере.

В этом разделе мы создадим полицейский радар скорости, который сообщает о том, превышена ли скорость.

2.11.1 Добавим ещё один input

2.11.2 Функции конвертации скорости

Для правильной синхронизации, нам понадобятся функции конвертации скорости из км/ч в миль/ч и наоборот:

Также она будет обеспечивать точность до двух знаков после запятой и возвращать пустую строку, если пользователь ввёл невалидное значение скорости. Для улучшения читабельности кода вынесем валидацию скорости в отдельную функцию:

2.11.3 Подъём состояния выше по иерархии

Сейчас оба наших компонента SpeedSetter хранят собственные значения скорости в своём локальном состоянии независимо друг от друга:

Согласно требованиям нам их нужно синхронизировать между собой. Как только будет обновлён установщик «км/ч», установщик «миль/ч» тут же отобразит конвертированное значение, и наоборот.

Чтобы несколько компонентов React могли совместно использовать одно состояние, нужно это состояние поместить в их ближайший общий предок. Это называется «подъём состояния» (в оригинале «lifting state up»).

Давайте пошагово рассмотрим как это работает.

Мы будем хранить текущую введенную скорость speed и единицу измерения unit в его локальном состоянии. Это состояние мы «подняли» из установщиков скорости. Теперь оно будет служить для них «единственным источником истины». Это минимальное представление всех данных, которые нам необходимо знать, чтобы отрисовать оба установщика.

К примеру, если мы вводим значение 40 в установщик «Км/ч», то состояние компонента SpeedRadar будет:

Теперь наши установщики скорости синхронизированы, так как их значения вычислены из одного и того же состояния:

Давайте прорезюмируем, что происходит, когда мы редактируем input:

Внутри этих методов, компонент SpeedRadar запрашивает у React перерисовку, используя вызов this.setState() с новым введенным значением скорости и её единицей измерения.

Каждое обновление проходит по точно такому же алгоритму, так что элементы всегда остаются синхронизированными.

2.11.4 Извлеченные уроки

В приложении React должен существовать лишь один «источник истины» для любых данных, которые изменяются с течением времени. Обычно состояние сначала добавляется в компонент, которому оно необходимо для отрисовки. Затем, если другие компоненты тоже требуют данные этого состояния, вы можете «поднять» его к их ближайшему общему предку. Не пытайтесь синхронизировать состояние между различными компонентами, вместо этого полагайтесь на нисходящий поток данных.

Если вы видите что-то неправильное в UI, вы можете использовать React Developer Tools, чтобы проинспектировать свойства и подняться вверх по дереву до тех пор, пока не найдете компонент, ответственный за обновление состояния. Это позволит вам выследить источник багов.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *