Что такое state react
Состояние state и события в React
Учебник по React
Учебник Angular 1.5.8
Сейчас мы с вами освоим работу с событиями в фреймворке React, а также подробнее поговорим о свойствах и методах объектов-компонентов.
State
Основным понятием React является state (состояние, стейт).
В state хранится текущее состояние компонента в виде объекта <> с ключами и значениями. Что входит в понятие текущее состояние?
Во-первых, это какие-либо данные. Например, компонент показывает на экране список имен юзеров. Эти имена изначально должны где-то хранится, обычно в каком-то массиве. Вот этот массив и должен хранится в state.
Работа со state
Давайте выведем данные из стейта при рендеринге, то есть в методе render():
Результатом работы этого кода будет следующее:
Таким образом, в конструкторе нашего класса мы можем записывать какие-то данные в стейт, а затем выводить их в других методах (чаще всего в методе render, но не обязательно), обращаясь к ним через this.state.имяСостояния.
Работа с событиями
Давайте теперь изучим работу с событиями на React. К примеру, сделаем так, чтобы по клику на блок выводился алерт с некоторым текстом.
Пусть у нас есть метод showMessage, который выводит алерт с сообщением, а в методе render у нас есть div, по клику на который нам и хотелось бы видеть этот алерт:
Давайте теперь привяжем к нашему диву событие onclick так, чтобы по клику на этот див срабатывал метод showMessage.
Для этого мы напишем следующее:
Теперь по клику на див будет выводиться сообщение:
Таким образом и происходит работа с событиями: добавляется атрибут (к примеру, onClick или onFocus), значением атрибута указывается метод, который будет вызван по этому событию.
Работа с this
Пусть теперь в конструкторе у нас задан this.state, в котором хранится имя и фамилия пользователя:
Давайте сделаем так, чтобы метод showMessage выводил имя нашего пользователя, хранящегося в стейте (то есть ‘Иван’). Кажется, что в методе showMessage следует поменять alert(‘Привет!’) на alert(this.state.name), но это не будет работать:
Теперь все будет работать, как надо:
Если вы запустите этот код и нажмете на div, то все будет работать так, как и работало: вы увидите алерт с сообщением ‘Иван’.
Вы можете пользоваться тем способом, который вам кажется более удобным.
Изменение стейта
Пусть в this.state хранится имя пользователя:
Давайте в методе render выведем его на экран:
Давайте теперь сделаем кнопку, по нажатию на которую будет меняться имя пользователя. Пусть по клику на эту кнопку вызывается метод changeName:
Обратите также внимание на .bind(this) в строчке onClick=
С помощью этого метода мы сможем поменять значение this.state.name, вот так: this.setState(
Почему именно так, а не напрямую: потому что в этом случае все места вставки вида сменят свое значение.
То есть: после вызова this.setState(
Давайте соберем все вместе:
Примечание
Если в стейте несколько значений, то в this.setState мы передаем только те, которые хотим поменять. Пример: пусть в стейте кроме имени хранится еще и фамилия, но мы по прежнему хотим менять только имя:
Если вы запустите этот код и нажмете на div, то все будет работать так, как и работало: ‘Иван’ мгновенно поменяется на ‘Коля’.
Но никто не мешает нам поменять несколько значений:
Что вам делать дальше:
Приступайте к решению задач по следующей ссылке: задачи к уроку.
Component State
What does setState do?
setState() schedules an update to a component’s state object. When state changes, the component responds by re-rendering.
props (short for “properties”) and state are both plain JavaScript objects. While both hold information that influences the output of render, they are different in one important way: props get passed to the component (similar to function parameters) whereas state is managed within the component (similar to variables declared within a function).
Here are some good resources for further reading on when to use props vs state :
Why is setState giving me the wrong value?
In React, both this.props and this.state represent the rendered values, i.e. what’s currently on the screen.
Example of code that will not behave as expected:
See below for how to fix this problem.
How do I update state with values that depend on the current state?
Pass a function instead of an object to setState to ensure the call always uses the most updated version of state (see below).
Passing an update function allows you to access the current state value inside the updater. Since setState calls are batched, this lets you chain updates and ensure they build on top of each other instead of conflicting:
When is setState asynchronous?
Currently, setState is asynchronous inside event handlers.
This ensures, for example, that if both Parent and Child call setState during a click event, Child isn’t re-rendered twice. Instead, React “flushes” the state updates at the end of the browser event. This results in significant performance improvements in larger apps.
This is an implementation detail so avoid relying on it directly. In the future versions, React will batch updates by default in more cases.
Why doesn’t React update this.state synchronously?
As explained in the previous section, React intentionally “waits” until all components call setState() in their event handlers before starting to re-render. This boosts performance by avoiding unnecessary re-renders.
However, you might still be wondering why React doesn’t just update this.state immediately without re-rendering.
There are two main reasons:
This GitHub comment dives deep into the specific examples.
Should I use a state management library like Redux or MobX?
It’s a good idea to get to know React first, before adding in additional libraries. You can build quite complex applications using only React.
2.6 Состояние и жизненный цикл
В этом разделе мы расскажем о таких важных концепциях, как состояние и жизненный цикл компонента React. Более подробный API компонента вы можете найти здесь.
Рассмотрим, упомянутый ранее, пример тикающих часов.
Пока что мы знаем только один способ обновления UI.
В этом разделе мы сделаем компонент Timer по-настоящему переиспользуемым и инкапсулированным. Он сначала установит собственный таймер, а затем станет периодически обновляться через определенный промежуток времени.
Давайте начнём с инкапсуляции кода в компонент Timer :
В идеале, нам необходимо спроектировать самообновляющийся компонент Timer так, чтобы код, который его использует имел следующий вид:
Чтобы этого добиться, к компоненту Timer нужно добавить состояние.
Состояние похоже на свойства props, однако является приватным и полностью контролируется компонентом.
Раньше состоянием могли обладать только компоненты-классы. Однако с появлением хуков состоянием могут обладать и компоненты-функции.
2.6.1 Преобразование функций в классы
Мы можем преобразовать компонент-функцию Timer в класс за пять шагов:
Теперь компонент Timer определён как класс, а не как функция.
2.6.2 Добавление локального состояния в класс
Давайте переместим date из props в state в три этапа.
1. Заменим this.props.value на this.state.value в методе render() :
2. Добавим конструктор класса, который устанавливает начальное состояние this.state :
Обратите внимание на то, как мы передаем свойства props в базовый конструктор:
3. Удаляем свойство value из элемента:
Позже мы добавим код таймера обратно в сам компонент.
Результат будет выглядеть следующим образом:
Далее мы сделаем так, что компонент Timer будет устанавливать таймер и обновлять себя каждую секунду.
2.6.3 Добавление методов жизненного цикла в класс
При старте приложения React, компонент Timer будет впервые отрисован в DOM. В React это называется монтированием/монтажом компонента.
После каждого монтирования Timer ему нужно устанавливать таймер, чтобы периодически себя обновлять.
Однако в приложениях с множеством компонентов очень важно высвобождать ресурсы, занятые компонентами, когда они уничтожаются, чтобы избежать утечек памяти.
React позволяет объявить в компоненте-классе специальные методы, чтобы запускать определенный код, когда компонент монтируется или демонтируется:
В документации эти методы называются «lifecycle hooks». Мы же для простоты будем называть их методами жизненного цикла (ЖЦ).
Мы будем очищать таймер в методе жизненного цикла componentWillUnmount() :
Теперь компонент постоянно обновляется через установленный промежуток времени.
Давайте подытожим всё, что произошло, а также порядок, в котором вызываются методы:
Когда результат отрисовки Timer вставлен в DOM, React вызывает метод componentDidMount() жизненного цикла. Внутри него компонент Timer обращается к браузеру для установки таймера, чтобы вызывать increment() раз в секунду.
Если компонент Timer в какой-то момент удалён из DOM, React вызывает метод componentWillUnmount() жизненного цикла, из-за чего таймер останавливается.
2.6.4 Корректное обновление состояния
О setState() нужно знать три вещи.
2.6.4.1 Не модифицируйте состояние напрямую
К примеру, этот компонент перерисовываться не будет:
Для корректной модификации состояния компонента используйте метод setState() :
Вы можете установить this.state только в конструкторе!
2.6.4.2 Обновления состояния могут быть асинхронными
React может собирать последовательность вызовов setState() в единое обновление в целях повышения производительности.
Так как React может обновлять this.props и this.state асинхронно, вы не должны полагаться на их значения для вычисления следующего состояния.
К примеру, такой код может не обновить температуру:
Мы использовали стрелочную функцию, но можно использовать и обычные функции:
2.6.4.3 Обновления состояния объединяются
Например, состояние вашего компонента может содержать множество независимых переменных:
Далее вы можете обновить их независимо с помощью отдельных вызовов setState() :
2.6.5 Нисходящий поток данных
Вот почему состояние часто называют локальным или инкапсулированным. Оно недоступно для какого-либо компонента, за исключением того, который им владеет и устанавливает.
Компонент может решить передать это состояние вниз как свойства props своим дочерним компонентам:
Таким же образом это работает и для пользовательских компонентов:
Это принято называть «сверху-вниз», «нисходящим» или однонаправленным потоком данных. Любое состояние всегда находится во владении какого-либо компонента. Любые данные или UI, производные от этого состояния могут передаваться только в компоненты «ниже» их в дереве иерархии.
Если представить дерево компонентов как «водопад» свойств, то состояние каждого компонента является подобием дополнительного источника воды, который соединяется с водопадом в произвольной точке и также течет вниз.
Каждый компонент устанавливает своё собственное значение и обновляется независимо.
В приложениях React, независимо от того, обладает ли компонент состоянием – состояние является деталью реализации этого компонента и может изменяться со временем. Вы можете использовать компоненты без состояния внутри компонентов, имеющих состояние, и наоборот.
Использование хука состояния
Хуки — нововведение в React 16.8, которое позволяет использовать состояние и другие возможности React без написания классов.
На странице введения в хуки мы познакомились с ними на этом примере:
Давайте начнём изучать хуки, сравнив этот код с эквивалентным кодом на основе класса.
Эквивалентный пример с классом
Если вы уже пользовались классами в React, то вам знаком такой код:
Возможно, вы спросите себя, почему мы используем в качестве примера счётчик, а не что-то более реалистичное. Дело в том, что мы хотим обратить ваше внимание на API, одновременно делая первые шаги с хуками.
Хуки и функциональные компоненты
Напоминаем, что функциональные компоненты в React выглядят так:
Возможно, вы слышали, что такие компоненты называются «компонентами без состояния». Сейчас мы покажем, как использовать внутри них состояние React, поэтому будем называть их «функциональными компонентами».
Хуки НЕ работают внутри классов, а используются вместо них.
Наш новый пример начинается с того, что импортирует хук useState из React:
Что такое хук? Хук — это специальная функция, которая позволяет «подцепиться» к возможностям React. Например, хук useState предоставляет функциональным компонентам доступ к состоянию React. Мы узнаем про другие хуки чуть позже.
Когда применить хук? Раньше, если вы писали функциональный компонент и осознавали, что вам нужно наделить его состоянием, вам приходилось превращать этот компонент в класс. Теперь же вы можете использовать хук внутри существующего функционального компонента. Мы покажем это прямо сейчас!
Есть специальные правила о том, где можно, а где нельзя использовать хуки внутри компонента. Их мы изучим в главе Правила хуков.
Объявление переменной состояния
Слово «create» («создать») было бы не совсем точно, потому что состояние создаётся только в момент, когда компонент рендерится впервые. В последующие же рендеринги useState возвращает текущее состояние. Иначе не существовало бы «состояния» как такового. Названия всех хуков начинаются с «use» тоже неспроста. О причине мы узнаем из Правил хуков.
Когда мы хотим отобразить текущее состояние счётчика в классе, мы обращаемся к this.state.count :
В функции же мы можем использовать count напрямую:
Давайте построчно пробежимся по тому, что мы выучили и проверим наши знания:
Поначалу это всё может показаться слишком сложным. Не торопитесь! Если вы запутались в объяснении, ещё раз прочитайте приведённый код с начала до конца. Обещаем, если вы на минутку «забудете», как состояние работает в классах, и посмотрите на код свежим взглядом, всё станет ясно.
Совет: Что делают квадратные скобки?
Вы могли обратить внимание на квадратные скобки в месте, где объявляется переменная состояния:
Два имени в квадратных скобках не содержатся в API React. Названия переменным состояния выбираете вы:
Совет: Использование нескольких переменных состояния
Объявлять переменные состояния через пару [something, setSomething] удобно ещё и тем, что когда нам нужны несколько переменных, мы можем назвать каждую из них собственным именем:
Использовать несколько переменных состояния совсем не обязательно, потому что они могут быть объектами или массивами, которые группируют связанные по смыслу данные. Обратите внимание, что, в отличие от this.setState в классах, обновление переменной состояния всегда замещает её значение, а не осуществляет слияние.
Подробные рекомендации о разделении независимых переменных состояния вы найдёте в FAQ.
А теперь давайте перейдём к изучению хука useEffect , похожего на методы жизненного цикла в классах. С его помощью компоненты могут выполнять побочные эффекты.
React: основные подходы к управлению состоянием
Доброго времени суток, друзья!
Предлагаю вашему вниманию простое приложение — список задач. Что в нем особенного, спросите вы. Дело в том, что я попытался реализовать одну и ту же «тудушку» с использованием четырех разных подходов к управлению состоянием в React-приложениях: useState, useContext + useReducer, Redux Toolkit и Recoil.
Начнем с того, что такое состояние приложения, и почему так важен выбор правильного инструмента для работы с ним.
Состояние — это собирательное понятие для любой информации, имеющей отношение к приложению. Это могут быть как данные, используемые в приложении, такие как тот же список задач или список пользователей, так и состояние как таковое, например, состояние загрузки или состояние формы.
Условно, состояние можно разделить на локальное и глобальное. Под локальным состоянием, обычно, понимается состояние отдельно взятого компонента, например, состояние формы, как правило, является локальным состоянием соответствующего компонента. В свою очередь, глобальное состояние правильнее именовать распределенным или совместно используемым, подразумевая под этим то, что такое состояние используется более чем одним компонентом. Условность рассматриваемой градации выражается в том, что локальное состояние вполне может использоваться несколькими компонентами (например, состояние, определенное с помощью useState(), может в виде пропов передаваться дочерним компонентам), а глобальное состояние не обязательно используется всеми компонентами приложения (например, в Redux, где имеется одно хранилище для состояния всего приложения, обычно, создается отдельный срез (slice) состояния для каждой части UI, точнее, для логики управления этой частью).
Важность выбора правильного инструмента для управления состоянием приложения обусловлена теми проблемами, которые возникают при несоответствии инструмента размерам приложения или сложности реализуемой в нем логики. Мы убедимся в этом в процессе разработки списка задач.
Я не буду вдаваться в подробности работы каждого инструмента, а ограничусь общим описанием и ссылками на соответствующие материалы. Для прототипирования UI будет использоваться react-bootstrap.
Создаем проект с помощью Create React App:
useState()
Хук «useState()» предназначен для управления локальным состоянием компонента. Он возвращает массив с двумя элементами: текущим значением состояния и сеттером — функцией для обновления этого значения. Сигнатура данного хука:
Пока ограничимся четырьмя базовыми операциями: добавление, переключение (выполнение), обновление и удаление задачи, но усложним себе жизнь тем, что наше начальное состояние будет иметь форму нормализованных данных (это позволит как следует попрактиковаться в иммутабельном обновлении).
Думаю, тут все понятно.
В App.js мы с помощью useState() определяем начальное состояние приложения, импортируем и рендерим компоненты приложения, передавая им состояние и сеттер в виде пропов:
В TodoForm.js мы реализуем добавление новой задачи в список:
В TodoList.js мы просто рендерим список элементов:
Наконец, в TodoListItem.js происходит самое интересное — здесь мы реализуем оставшиеся операции: переключение, обновление и удаление задачи:
Заключение
Итак, мы с вами реализовали список задач с использованием четырех разных подходов к управлению состоянием. Какие выводы можно из всего этого сделать?
Я выскажу свое мнение, оно не претендует на статус истины в последней инстанции. Разумеется, выбор правильного инструмента для управления состоянием зависит от задач, решаемых приложением: