Валидация форм в React-приложениях с помощью хука useForm
Статья (англ.): https://blog.openreplay.com/react-form-validation-with-the-useform-hook
Валидация форм в вебе - очень важная задача, которая так до сих пор и не стандартизирована полноценно. Существуют десятки разных решений, и в статье разбирается одно из них - библиотека React Hook Form https://react-hook-form.com/get-started.
Эта библиотека небольшая, производительная и довольно популярная, как понятно из названия она использует хуки, которые де-факто являются сейчас стандартом в React разработке.
Предлагаемый библиотекой api интуитивно понятный, хоть и немного громоздкий (впрочем, где вы видели негромоздкую валидацию, да?)
https://codesandbox.io/s/react-hook-form-react-junior-qyv1if?file=/src/App.js
Все базовые потребности есть: установка правил валидации, вывод ошибок, отслеживание изменений, отслеживание фокуса и т.д.
Использование
Хук
*
* в
*
Регистрация поля
Функция
Кроме того, можно установить собственную функцию для валидации.
formState
Содержит флаги
#ссылки #валидацияформ
Статья (англ.): https://blog.openreplay.com/react-form-validation-with-the-useform-hook
Валидация форм в вебе - очень важная задача, которая так до сих пор и не стандартизирована полноценно. Существуют десятки разных решений, и в статье разбирается одно из них - библиотека React Hook Form https://react-hook-form.com/get-started.
Эта библиотека небольшая, производительная и довольно популярная, как понятно из названия она использует хуки, которые де-факто являются сейчас стандартом в React разработке.
Предлагаемый библиотекой api интуитивно понятный, хоть и немного громоздкий (впрочем, где вы видели негромоздкую валидацию, да?)
https://codesandbox.io/s/react-hook-form-react-junior-qyv1if?file=/src/App.js
Все базовые потребности есть: установка правил валидации, вывод ошибок, отслеживание изменений, отслеживание фокуса и т.д.
Использование
Хук
useForm
может принимать базовые настройки валидации (можно ничего не передавать и использовать дефолтные) и возвращает несколько методов (`register`, handleSubmit`), а также объект `formState
. По названиям очевидно, для чего они предназначены:*
register
- регистрирует каждое валидируемое поле с конкретными настройками валидации.* в
handleSubmit
нужно передать функцию, которая будет вызвана при удачной отправке формы. Обычно такие методы вызываются на событие form.onsubmit
.*
formState
содержит актуальную информацию, включая ошибки валидации.Регистрация поля
Функция
register
принимает имя поля и объект с настройками валидации и возвращает набор атрибутов для контрола (для отслеживания фокуса и изменений значения). Есть ряд базовых опций, таких как required
, minLength
, maxLength
, pattern
. Для каждой настройки можно просто ставить true
или конкретное значение (например, минимальная длина или паттерн), а можно передать объект с полями value
и message
. Кроме того, можно установить собственную функцию для валидации.
formState
Содержит флаги
isSubmitted
, isValid
, объект с ошибками errors
и др.#ссылки #валидацияформ
Openreplay
React Form Validation with the useForm Hook
Learn how to add validation on your React forms using this detailed tutorial
👍1👏1
Статус асинхронного запроса
В документации Redux описан хороший паттерн работы с асинхронными запросами. Он не относится непосредственно к Redux, применять его можно где угодно.
Очень часто для отслеживания состояния запроса используют флаги типа
Подробнее в документации: https://redux.js.org/tutorials/fundamentals/part-7-standard-patterns#async-request-status
Пример: https://codesandbox.io/s/async-request-status-react-junior-itoqep?file=/src/store/index.js
#redux #документация #паттерны #примерыкода
В документации Redux описан хороший паттерн работы с асинхронными запросами. Он не относится непосредственно к Redux, применять его можно где угодно.
Очень часто для отслеживания состояния запроса используют флаги типа
isLoading
, isLoaded
, но намного удобнее иметь дело со строковыми статусами: idle
, loading
, succeeded
, failed
. Так проще охватить все возможные состояния и проанализировать их для выведения нужного представления. Подробнее в документации: https://redux.js.org/tutorials/fundamentals/part-7-standard-patterns#async-request-status
Пример: https://codesandbox.io/s/async-request-status-react-junior-itoqep?file=/src/store/index.js
#redux #документация #паттерны #примерыкода
redux.js.org
Redux Fundamentals, Part 7: Standard Redux Patterns | Redux
The official Fundamentals tutorial for Redux: learn the standard patterns used in real-world Redux apps
👏2
Flux Standard Actions
Соглашение по структуре экшенов в Redux:
- экшен - это обычный JavaScript-объект
- у него обязательно есть поле
- любые данные должны лежать в поле
- кроме того у экшена может быть поле
- при необходимости экшен может иметь поле
#документация #redux #паттерны
Соглашение по структуре экшенов в Redux:
- экшен - это обычный JavaScript-объект
- у него обязательно есть поле
type
- любые данные должны лежать в поле
payload
- кроме того у экшена может быть поле
meta
для различной описательной информации- при необходимости экшен может иметь поле
error
(булево значение). Если оно равно true
, экшен представляет собой ошибку и является аналогом промиса в состоянии rejected
. В этом случае в поле payload
должен находиться объект ошибки.#документация #redux #паттерны
👍2👏1
Нормализация состояния
Во всех предыдущих примерах элементы хранились в store в виде массива. Но в больших проектах со сложным состоянием разумно использовать другую структуру данных - словарь с доступом по идентификатору (обычный JavaScript-объект).
Это способ "нормализовать" состояние.
Цель нормализации состоит прежде всего в том, чтобы избежать копирования данных.
Допустим, у нас есть массив пользователей и массив постов, которые можно лайкать. Для каждого поста нужно сохранять список пользователей, которые его лайкали. Разумнее сохранять только идентификаторы этих пользователей, а не копировать их полностью. Ведь если придется вносить какие-то изменения, их придется вносить в нескольких местах, что увеличивает вероятность ошибки. При необходимости мы всегда можем получить полные данные пользователя, и удобнее это делать из объекта-словаря (время доступа О(1)), чем из массива (время доступа О(n)).
Тут понятно, пользователи и посты мало связаны друг с другом, так что их можно хранить отдельно в разных словарях. Другой пример, возможно, чуть менее очевидный - посты и комментарии к постам. Вроде бы комментарии - часть постов и вполне могут храниться прямо в объекте поста, однако большая вложенность структуры затрудняет любые операции с этими комментариями. Поэтому также разумно хранить комментарии в отдельном словаре с доступом по идентификатору, а в посте - только массив идентификаторов.
При использовании объектов некоторые операции в редьюсере могут стать более громоздкими (например, получение списка). А другие, наоборот, более компактными (например, изменение или удаление элемента). В целом для приложений со сложными данными нормализация более чем окупается.
Раздел в документации (англ.): https://redux.js.org/tutorials/fundamentals/part-7-standard-patterns#normalized-state
#redux #документация #паттерны
Во всех предыдущих примерах элементы хранились в store в виде массива. Но в больших проектах со сложным состоянием разумно использовать другую структуру данных - словарь с доступом по идентификатору (обычный JavaScript-объект).
Это способ "нормализовать" состояние.
Цель нормализации состоит прежде всего в том, чтобы избежать копирования данных.
Допустим, у нас есть массив пользователей и массив постов, которые можно лайкать. Для каждого поста нужно сохранять список пользователей, которые его лайкали. Разумнее сохранять только идентификаторы этих пользователей, а не копировать их полностью. Ведь если придется вносить какие-то изменения, их придется вносить в нескольких местах, что увеличивает вероятность ошибки. При необходимости мы всегда можем получить полные данные пользователя, и удобнее это делать из объекта-словаря (время доступа О(1)), чем из массива (время доступа О(n)).
Тут понятно, пользователи и посты мало связаны друг с другом, так что их можно хранить отдельно в разных словарях. Другой пример, возможно, чуть менее очевидный - посты и комментарии к постам. Вроде бы комментарии - часть постов и вполне могут храниться прямо в объекте поста, однако большая вложенность структуры затрудняет любые операции с этими комментариями. Поэтому также разумно хранить комментарии в отдельном словаре с доступом по идентификатору, а в посте - только массив идентификаторов.
При использовании объектов некоторые операции в редьюсере могут стать более громоздкими (например, получение списка). А другие, наоборот, более компактными (например, изменение или удаление элемента). В целом для приложений со сложными данными нормализация более чем окупается.
Раздел в документации (англ.): https://redux.js.org/tutorials/fundamentals/part-7-standard-patterns#normalized-state
#redux #документация #паттерны
redux.js.org
Redux Fundamentals, Part 7: Standard Redux Patterns | Redux
The official Fundamentals tutorial for Redux: learn the standard patterns used in real-world Redux apps
👍3
Возращение промиса из thunk
И еще один полезный паттерн, который предлагает нам документация Redux.
Для выполнения асинхронных экшенов мы используем пакет thunk, который позволяет передавать в
Для таких асинхронных запросов часто нужно отслеживать состояние запроса (в процессе/завершен). Мы уже разбирали, что удобно хранить это состояние в виде строки вместо булевых флагов.
Это состояние можно хранить в глобальном хранилище, но все-таки чаще ему место в компоненте.
Например, возьмем обычный TODO-лист. При добавлении нового элемента в список мы отправляем запрос на сервер для его сохранения. Пока этот запрос выполняется, нужно показывать лоадер, а когда запрос завершен, скрывать его.
Уместо поместить логику отображения лоадера в сам компонент, а не в глобальное хранилище. И это сделать очень просто.
Когда мы передаем в
Схема работы
https://codesandbox.io/s/thunks-and-promises-react-junior-5edjgl?file=/src/store.js
Функция
При добавлении нового элемента вызываем
В
Таким образом, паттерн заключается в том, что из thunk-функции мы возвращаем промис, который используется непосредственно в компоненте.
#redux #управлениесостоянием #примерыкода #паттерны
И еще один полезный паттерн, который предлагает нам документация Redux.
Для выполнения асинхронных экшенов мы используем пакет thunk, который позволяет передавать в
dispatch
функции вместо объектов. Это удобно, например, делать для ajax-запросов к серверу.Для таких асинхронных запросов часто нужно отслеживать состояние запроса (в процессе/завершен). Мы уже разбирали, что удобно хранить это состояние в виде строки вместо булевых флагов.
Это состояние можно хранить в глобальном хранилище, но все-таки чаще ему место в компоненте.
Например, возьмем обычный TODO-лист. При добавлении нового элемента в список мы отправляем запрос на сервер для его сохранения. Пока этот запрос выполняется, нужно показывать лоадер, а когда запрос завершен, скрывать его.
Уместо поместить логику отображения лоадера в сам компонент, а не в глобальное хранилище. И это сделать очень просто.
Когда мы передаем в
dispatch
функцию, thunk возвращает результат ее выполнения. Этим результатом вполне может быть промис, ориентируясь на состояни которого мы можем отображать или скрывать лоадер.Схема работы
https://codesandbox.io/s/thunks-and-promises-react-junior-5edjgl?file=/src/store.js
Функция
addNewTodo(text)
это создатель экшена, она возвращает thunk вместо обычного объекта.
function addNewTodo(text) {
return function (dispatch) {
return new Promise(function(resolve) {
// запрос на сервер
dispatch(...)
resolve()
})
}
}
При добавлении нового элемента вызываем
dispatch(addNewTodo(text))
. В
dispatch
попадает функция, а не объект, поэтому она сразу же запускается, а результат этого запуска возвращается.
const [ loading, setLoading ] = useState(false)
const handleSubmit = function() {
setLoading(true)
dispatch(addNewTodo(text)).then(function() {
setLoading(false);
})
}
Таким образом, паттерн заключается в том, что из thunk-функции мы возвращаем промис, который используется непосредственно в компоненте.
#redux #управлениесостоянием #примерыкода #паттерны
Telegram
React Junior
Thunk
И наконец, у Redux есть официальный миддлвар для обработки функциональных экшенов - пакет redux-thunk. Можно заменить нашу самописную handleFunction на него:
https://codesandbox.io/s/react-redux-thunk-react-junior-d71jls?file=/src/store/index.js
…
И наконец, у Redux есть официальный миддлвар для обработки функциональных экшенов - пакет redux-thunk. Можно заменить нашу самописную handleFunction на него:
https://codesandbox.io/s/react-redux-thunk-react-junior-d71jls?file=/src/store/index.js
…
👍3
Redux Toolkit
Документация Redux очень настойчиво советует использовать в разработке Redux Toolkit, так что я более не могу его игнорировать. Последняя часть руководства Redux Fundamentals как раз посвящена этому инструменту.
Предполагается, что Redux Toolkit позволить упростить работу с хранилищем Redux, а также убережет нас от большинства распространенных ошибок.
Важно: Redux Toolkit - это именно про Redux, то есть про глобальное хранилище. Он не имеет никакого отношения к интерфейсу и связке Redux + React.
По сути, Redux Toolkit - это набор утилит, которые позволяют сократить объем шаблонного кода:
Вот эти утилиты:
-
-
-
-
Будем разбираться с каждой отдельно и сравнивать новый и старый код.
#redux #управлениесостоянием #документация
Документация Redux очень настойчиво советует использовать в разработке Redux Toolkit, так что я более не могу его игнорировать. Последняя часть руководства Redux Fundamentals как раз посвящена этому инструменту.
Предполагается, что Redux Toolkit позволить упростить работу с хранилищем Redux, а также убережет нас от большинства распространенных ошибок.
Важно: Redux Toolkit - это именно про Redux, то есть про глобальное хранилище. Он не имеет никакого отношения к интерфейсу и связке Redux + React.
По сути, Redux Toolkit - это набор утилит, которые позволяют сократить объем шаблонного кода:
Вот эти утилиты:
-
configureStore
-
createSlice
-
createAsyncThunk
-
createEntityAdapter
Будем разбираться с каждой отдельно и сравнивать новый и старый код.
#redux #управлениесостоянием #документация
redux.js.org
Redux Fundamentals, Part 1: Redux Overview | Redux
The official Fundamentals tutorial for Redux: learn the fundamentals of using Redux
👍6
Redux Toolkit. Создание хранилища
Первая функция из Redux Toolkit -
Вспоминаем, как это происходит в обычном redux:
https://codesandbox.io/s/redux-toolkit-demo-react-junior-4ihz98?file=/src/store.js
1. Объединяем несколько отдельных редьюсеров (слайсов) в один общий
2. Подключаем thunk, applyMiddleware и composeWithDevTools, чтобы собрать полностью укомплектованное возможностями хранилище.
3. Совмещаем это все в функции
* Логика внутри слайсов, а также разделение кода по файлам сейчас не имеет значения. Разбираемся только в создании хранилища.
Как выглядит создание хранилища с
https://codesandbox.io/s/redux-toolkit-configure-store-react-junior-iv0vpk?file=/src/store.js
1. Передаем функции отдельные редьюсеры - получаем готовое хранилище.
Под капотом происходит объединение редьюсеров-слайсов в один корневой редьюсер, подключение thunk и интеграция с redux-devtools-extension. Помимо этого, добавляются дополнительные миддлвары, которые позволяют отслеживать распространенные ошибки, например, мутации состояния.
Таким образом, мы заменяем три пакета (redux, redux-devtools-extension, redux-thunk) на один (@reduxjs/toolkit).
#redux #паттерны #управлениесостоянием #документация #примерыкода
Первая функция из Redux Toolkit -
configureStore
- предназначена для создания хранилища.Вспоминаем, как это происходит в обычном redux:
https://codesandbox.io/s/redux-toolkit-demo-react-junior-4ihz98?file=/src/store.js
1. Объединяем несколько отдельных редьюсеров (слайсов) в один общий
rootReducer
, который будет обрабатывать каждый экшен (с помощью функции combineReducers).2. Подключаем thunk, applyMiddleware и composeWithDevTools, чтобы собрать полностью укомплектованное возможностями хранилище.
3. Совмещаем это все в функции
createStore
.* Логика внутри слайсов, а также разделение кода по файлам сейчас не имеет значения. Разбираемся только в создании хранилища.
Как выглядит создание хранилища с
configureStore
:https://codesandbox.io/s/redux-toolkit-configure-store-react-junior-iv0vpk?file=/src/store.js
1. Передаем функции отдельные редьюсеры - получаем готовое хранилище.
Под капотом происходит объединение редьюсеров-слайсов в один корневой редьюсер, подключение thunk и интеграция с redux-devtools-extension. Помимо этого, добавляются дополнительные миддлвары, которые позволяют отслеживать распространенные ошибки, например, мутации состояния.
Таким образом, мы заменяем три пакета (redux, redux-devtools-extension, redux-thunk) на один (@reduxjs/toolkit).
#redux #паттерны #управлениесостоянием #документация #примерыкода
Telegram
React Junior
Redux: начало
Начнем как всегда с начала, то есть с документации: https://redux.js.org/introduction/getting-started. Она, кстати, очень большая, с кучей разных туториалов и объяснений.
Итак, основные части Redux:
Хранилище (store)
Все глобальное состояние…
Начнем как всегда с начала, то есть с документации: https://redux.js.org/introduction/getting-started. Она, кстати, очень большая, с кучей разных туториалов и объяснений.
Итак, основные части Redux:
Хранилище (store)
Все глобальное состояние…
👍2
Redux Toolkit. Создание хранилища. Защита от мутаций
Функция
Если вместо копирования, мы попробуем мутировать текущее состояние, то будет ошибка
#redux #паттерны #управлениесостоянием #документация #примерыкода
Функция
configureStore
делает много полезных вещей под капотом, например, подключает redux-thunk и redux-devtools-extensions. Кроме того, она добавляет миддлвар, который отслеживает мутацию стейта в редьюсере.Если вместо копирования, мы попробуем мутировать текущее состояние, то будет ошибка
// Мутация состояния, Ошибка
state.active = !state.active;
return state;
// Копирование, нет ошибок
return {
...state,
active: !state.active
};
#redux #паттерны #управлениесостоянием #документация #примерыкода
👍2
Redux Toolkit. Слайсы и экшены
Переходим к отдельным редьюсерам, или слайсам (срезам). Будем рассматривать их вместе с экшенами, так как они тесно связаны, ведь редьюсеры обрабатывают экшены.
В обычном redux это довольно многословная часть кода: здесь и конструкция switch, и создатели экшенов.
https://codesandbox.io/s/redux-toolkit-demo-react-junior-4ihz98?file=/src/features/filters/slice.js
В демо-примере эта логика разделена на два файла для каждой фичи - actions.js с типами экшенов и функциями-создателями экшенов и slice.js с редьюсером и селекторами.
1. Определяем типы экшенов.
2. Пишем функции-создатели экшенов.
3. Для каждого типа экшена пишем отдельный кейс в редьюсере.
4. Не забываем передать в редьюсер исходное состояние для инициализации.
Redux Toolkit предлагает функцию
Вместо:
Будет вот такой код:
Более декларативно, удобнее читать отдельные кейсы, не нужно указывать избыточный дефолтный кейс.
Более того, в обработчиках можно мутировать параметр
Функция
Экшены
Более того, функция
Селекторы
Redux Toolkit самостоятельно импортирует модуль reselect (как хорошую практику работы с redux), поэтому функцию
Обновленный код
https://codesandbox.io/s/redux-toolkit-create-slice-react-junior-v37gp0?file=/src/features/filters/slice.js
В обновленном варианте мы можем избавиться от файла actions.js, так как создатели экшенов генерируются автоматически.
Вот обновленный вариант для слайса todos: https://codesandbox.io/s/redux-toolkit-create-slice-react-junior-v37gp0?file=/src/features/todos/slice.js
Здесь кроме обычных экшенов у нас есть еще один thunk - функция
#redux #паттерны #управлениесостоянием #документация #примерыкода
Переходим к отдельным редьюсерам, или слайсам (срезам). Будем рассматривать их вместе с экшенами, так как они тесно связаны, ведь редьюсеры обрабатывают экшены.
В обычном redux это довольно многословная часть кода: здесь и конструкция switch, и создатели экшенов.
https://codesandbox.io/s/redux-toolkit-demo-react-junior-4ihz98?file=/src/features/filters/slice.js
В демо-примере эта логика разделена на два файла для каждой фичи - actions.js с типами экшенов и функциями-создателями экшенов и slice.js с редьюсером и селекторами.
1. Определяем типы экшенов.
2. Пишем функции-создатели экшенов.
3. Для каждого типа экшена пишем отдельный кейс в редьюсере.
4. Не забываем передать в редьюсер исходное состояние для инициализации.
Redux Toolkit предлагает функцию
createSlice
, чтобы упростить эту задачу. Ей нужно передать строку-префикс для типов ("todos" из "todos/addTodo"), начальное состояние и функцию-обработчик для каждого кейса (экшена).Вместо:
function (state = initialState, action) {
switch(action.type) {
case 'action1'
case 'action2'
default
}
}
Будет вот такой код:
createSlice({
name: 'todos',
initialState,
reducers: {
action1(state, action) {
},
action2(state, action) {
}
}
})
Более декларативно, удобнее читать отдельные кейсы, не нужно указывать избыточный дефолтный кейс.
Более того, в обработчиках можно мутировать параметр
state
, они не обязаны быть чистыми функциями. При большой вложенности это очень удобно.Функция
createSlice
возвращает объект с несколькими полями. Готовый редьюсер находится в поле reducer
.Экшены
Более того, функция
createSlice
самостоятельно создает action creators для всех указанных типов экшенов. Они находятся в поле actions
. Этим функциям-создателям нужно передать то, что должно находиться у экшена в поле payload
.Селекторы
Redux Toolkit самостоятельно импортирует модуль reselect (как хорошую практику работы с redux), поэтому функцию
createSelector
можно брать прямо из @reduxjs/toolkit. Обновленный код
https://codesandbox.io/s/redux-toolkit-create-slice-react-junior-v37gp0?file=/src/features/filters/slice.js
В обновленном варианте мы можем избавиться от файла actions.js, так как создатели экшенов генерируются автоматически.
Вот обновленный вариант для слайса todos: https://codesandbox.io/s/redux-toolkit-create-slice-react-junior-v37gp0?file=/src/features/todos/slice.js
Здесь кроме обычных экшенов у нас есть еще один thunk - функция
addTodoThunk
. Она пока перенесена в старом виде, скоро мы ее заменим.#redux #паттерны #управлениесостоянием #документация #примерыкода
CodeSandbox
Redux Toolkit. Demo. React Junior - CodeSandbox
Redux Toolkit. Demo. React Junior by furrycat.web using react, react-dom, react-redux, react-scripts, redux, redux-devtools-extension, redux-thunk, reselect, styled-components
👍2
Redux Toolkit. Создатели экшенов
Создатели экшенов автоматически генерируются для всех экшенов, переданных в функцию
Но что если нам нужна функция посложнее, которая может принимать несколько аргументов и собирать из них
Например, для создания новой задачи в списке сейчас мы пишем:
Это можно устроить, немного развернув синтаксис функции
Вместо простой функции с аргументами
#redux #паттерны #управлениесостоянием #документация #примерыкода
Создатели экшенов автоматически генерируются для всех экшенов, переданных в функцию
createSlice
. const slice = createSlice({По умолчанию это самые простые функции. Они принимают один аргумент -
name: 'todos',
initialState,
reducers: {
todoAdded(state, action) {},
todoToggled(state, action) {}
}
});
// создатели экшенов находятся здесь
slice.actions; // { todoAdded, todoToggled }
payload
и формируют объект экшена с нужным типом.// создание экшенаСложные создатели экшенов
todoToggled(2); // { type: 'todos/todoToggled', payload: 2 }
Но что если нам нужна функция посложнее, которая может принимать несколько аргументов и собирать из них
payload
. Например, для создания новой задачи в списке сейчас мы пишем:
todoAdded(data)
, а хотели бы писать: todoAdded(id, text)
.Это можно устроить, немного развернув синтаксис функции
createSlice
. Вместо простой функции с аргументами
state
и action
редьюсер можно оформить в виде объекта. В поле reducer
передаем эту функцию без изменений, а в поле prepare
передаем функцию для формирования тела экшена.reducers: {Кроме
todoAdded: {
reducer(state, action) {},
prepare(id, text) {
return {
payload: {
id,
text
}
}
}
}
}
payload
тут можно указать еще поля meta
или error
- в соответствии с соглашением Flux Standart Action.todoAdded(2, 'todo text'); // { type: 'todos/todoAdded', payload: { id: 2, text: 'todo text' }}
#redux #паттерны #управлениесостоянием #документация #примерыкода
Telegram
React Junior
Flux Standard Actions
Соглашение по структуре экшенов в Redux:
- экшен - это обычный JavaScript-объект
- у него обязательно есть поле type
- любые данные должны лежать в поле payload
- кроме того у экшена может быть поле meta для различной описательной…
Соглашение по структуре экшенов в Redux:
- экшен - это обычный JavaScript-объект
- у него обязательно есть поле type
- любые данные должны лежать в поле payload
- кроме того у экшена может быть поле meta для различной описательной…
👍3👏1
Redux Toolkit. Создание thunks
Логика создания thunks в Redux Toolkit немного меняется.
Как было?
Раньше функция принимала в качестве аргумента
Как стало?
Redux Toolkit позволяет отделить логику обработки данных и логику обращения к хранилищу (ее она берет на себя).
Функция
Результат вызова
При этом под капотом в хранилище будут диспатчиться дополнительные экшены с типами:
-
-
-
Для каждого из них автоматически создается action creator:
-
-
-
Теперь нужно каким-то образом добавить обработку этих новых экшенов в редьюсере - то есть в функцию createSlice.
Добавление асинхронных экшенов в редьюсер
Самая громоздкая и трудная для понимания часть всего процесса.
В функцию
Для таких ситуаций есть еще поле
В следующем посте повторю все это еще раз с примерами из демо-приложения, потому что сложно так сразу огрокать.
#redux #паттерны #управлениесостоянием #документация #примерыкода
Логика создания thunks в Redux Toolkit немного меняется.
Как было?
Раньше функция принимала в качестве аргумента
dispatch
, сама производила все необходимые действия и сама же вызывала dispatch
в нужный момент.export function addTodoThunk(text) {
return (dispatch) => {
// обработка данных
return createTodo(text)
.then((data) => {
// обращение к хранилищу
dispatch(addTodo(data));
});
};
}
Как стало?
Redux Toolkit позволяет отделить логику обработки данных и логику обращения к хранилищу (ее она берет на себя).
Функция
createAsyncThunk
принимает два аргумента - имя, на основе которого будут формироваться типы экшенов, и собственно функцию для обработки данных. Эта функция должна возвращать промис, то есть внутри можно производить любые асинхронные действия. Можно использовать async-функции, которые всегда возвращают промис.export const saveNewTodo = createAsyncThunk(
"todos/saveNewTodo",
async function (text) {
return await createTodo(text);
}
);
Результат вызова
createAsyncThunk
- это обычный thunk creator. Вызываем его и передаем то, что вернулось, в dispatch
.dispatch(saveNewTodo("SOME TEXT"))
При этом под капотом в хранилище будут диспатчиться дополнительные экшены с типами:
-
todos/saveNewTodo/pending
(перед вызовом обработчика)-
todos/saveNewTodo/fulfilled
(если вызов завершится удачно)-
todos/saveNewTodo/rejected
(если будет ошибка)Для каждого из них автоматически создается action creator:
-
saveNewTodo.pending
-
saveNewTodo.fulfilled
-
saveNewTodo.rejected
Теперь нужно каким-то образом добавить обработку этих новых экшенов в редьюсере - то есть в функцию createSlice.
Добавление асинхронных экшенов в редьюсер
Самая громоздкая и трудная для понимания часть всего процесса.
В функцию
createSlice
мы передаем обработчики для каждого типа экшена (в поле reducers
). И для всех этих типов создаются action creators. Но тут у нас уже есть готовые action creators.Для таких ситуаций есть еще поле
extraReducers
. Это функция-коллбэк, которая принимает в качестве аргумента некий builder
. И вот в этот билдер с помощью метода addCase
нужно добавлять новые экшены с их обработчиками. Первым параметром нужно передать создатель экшена (функцию), вторым - обработчик (тоже функцию).createSlice({
// ...
extraReducers: function(builder) {
builder.addCase(
saveNewTodo.fulfilled,
function(state, action) {
// изменение state
};
}
})
В следующем посте повторю все это еще раз с примерами из демо-приложения, потому что сложно так сразу огрокать.
#redux #паттерны #управлениесостоянием #документация #примерыкода
Telegram
React Junior
Redux Toolkit. Слайсы и экшены
Переходим к отдельным редьюсерам, или слайсам (срезам). Будем рассматривать их вместе с экшенами, так как они тесно связаны, ведь редьюсеры обрабатывают экшены.
В обычном redux это довольно многословная часть кода: здесь…
Переходим к отдельным редьюсерам, или слайсам (срезам). Будем рассматривать их вместе с экшенами, так как они тесно связаны, ведь редьюсеры обрабатывают экшены.
В обычном redux это довольно многословная часть кода: здесь…
👍2
Redux Toolkit. Создание thunks - 2
https://codesandbox.io/s/redux-toolkit-create-async-thunk-react-junior-i9qc3c?file=/src/features/todos/slice.js
1. Асинхронная логика
Итак, сначала пишем собственно асинхронную логику и передаем ее функции
Помним, что функция должна возвращать промис, а первый аргумент - это префикс для типов экшенов.
В
Также у нее есть свойства:
Нам об этом думать не нужно, эти экшены будут создаваться и диспатчиться автоматически. В
2. Dispatch
Работаем с
3. Обработка в редьюсере
Чтобы обрабатывать новые экшены в редьюсере, добавляем в
Тут используем метод
* В демо примере убран экшен
#redux #паттерны #управлениесостоянием #документация #примерыкода
https://codesandbox.io/s/redux-toolkit-create-async-thunk-react-junior-i9qc3c?file=/src/features/todos/slice.js
1. Асинхронная логика
Итак, сначала пишем собственно асинхронную логику и передаем ее функции
createAsyncThunk
. Тут у нас запрос на сервер для сохранения новой задачи и получения ее идентификатора:export const saveNewTodo = createAsyncThunk(
"todos/saveNewTodo",
async (text) => {
return await createTodo(text);
}
);
Помним, что функция должна возвращать промис, а первый аргумент - это префикс для типов экшенов.
В
saveNewTodo
теперь лежит функция - thunk creator. Также у нее есть свойства:
saveNewTodo.pending
, saveNewTodo.fulfilled
и saveNewTodo.rejected
. В каждом из них лежит простая функция action creator, которая создает экшены с соответствующим типом (todos/saveNewTodo/pending, todos/saveNewTodo/fulfilled).Нам об этом думать не нужно, эти экшены будут создаваться и диспатчиться автоматически. В
.../fulfilled
экшен будет также передан результат работы нашей асинхронной функции.2. Dispatch
Работаем с
saveNewTodo
как с обычным thunk creator. dispatch(saveNewTodo(text))
3. Обработка в редьюсере
Чтобы обрабатывать новые экшены в редьюсере, добавляем в
createSlice
поле extraReducers
:const todosSlice = createSlice({
//...
extraReducers: function(builder) {
builder.addCase(
saveNewTodo.fulfilled,
function(state, action) {
state[action.payload.id] = {
...action.payload,
checked: false
}
}
)
}
})
Тут используем метод
builder.addCase(actionCreator, reducer)
. Можно выстраивать вызовы addCase
в цепочку, если требуется обработать несколько экшенов:builder
.addCase(saveNewTodo.pending, function(state, action) {})
.addCase(saveNewTodo.fulfilled, function(state, action) {})
* В демо примере убран экшен
todoAdded
, так как вся логика добавления нового элемента в список находится теперь внутри extraReducers
.#redux #паттерны #управлениесостоянием #документация #примерыкода
CodeSandbox
Redux Toolkit. Create Async Thunk. React Junior - CodeSandbox
Redux Toolkit. Create Async Thunk. React Junior by furrycat.web using @reduxjs/toolkit, react, react-dom, react-redux, react-scripts, styled-components
👍2
Было и стало.
Нельзя, конечно, сказать, что стало короче :) Но зато логика разделена более правильно (+ комментарии).
Обратите внимание, я тут не использую экшен '../pending', потому что состояние запроса отслеживается самим компонентом. Но его вполне можно вынести в общее хранилище и обрабатывать здесь.
#redux #управлениесостоянием #примерыкода #паттерны
Нельзя, конечно, сказать, что стало короче :) Но зато логика разделена более правильно (+ комментарии).
Обратите внимание, я тут не использую экшен '../pending', потому что состояние запроса отслеживается самим компонентом. Но его вполне можно вынести в общее хранилище и обрабатывать здесь.
#redux #управлениесостоянием #примерыкода #паттерны
👍2
Redux Toolkit. Операции с нормализованным состоянием
И наконец, последняя функция из Redux Toolkit, которую нужно разобрать в этом руководстве -
Пользоваться очень просто: вызов функции возвращает объект (адаптер) с кучей полезных методов.
Демо-проект: https://codesandbox.io/s/redux-toolkit-create-entity-adapter-react-junior-fipune?file=/src/features/todos/slice.js
Начальное состояние
Для получения начального состояния адаптера используем метод
Чтобы добавить дополнительные поля, нужно просто передать их в функцию:
Например, здесь я добавляю статус запроса к серверу. В итоге начальное состояние будет иметь три поля:
Манипуляции с данными
Адаптер предоставляет множество полезных методов для работы с коллекцией элементов:
-
-
-
-
-
Их можно использовать в редьюсерах, например, для кейса
Для наглядности в демо-пример добавлены еще два экшена:
Селекторы
Кроме того, адаптер предоставляет два готовых селектора:
#redux #управлениесостоянием #примерыкода #паттерны #документация
И наконец, последняя функция из Redux Toolkit, которую нужно разобрать в этом руководстве -
createEntityAdapter
. Она упрощает работу с нормализованным состоянием (когда мы храним не массив элементов, а объект, где ключами служат идентификаторы элементов).Пользоваться очень просто: вызов функции возвращает объект (адаптер) с кучей полезных методов.
const todosAdapter = createEntityAdapter();
/*
{
getInitialState,
addOne,
addMany,
removeOne,
...
}
*/
Демо-проект: https://codesandbox.io/s/redux-toolkit-create-entity-adapter-react-junior-fipune?file=/src/features/todos/slice.js
Начальное состояние
Для получения начального состояния адаптера используем метод
adapter.getInitialState
. Он вернет объект с полями entities
(объект-словарь) и ids
(массив идентификаторов).Чтобы добавить дополнительные поля, нужно просто передать их в функцию:
const initialState = todosAdapter.getInitialState({
status: 'idle'
})
Например, здесь я добавляю статус запроса к серверу. В итоге начальное состояние будет иметь три поля:
entities
, ids
и status
.Манипуляции с данными
Адаптер предоставляет множество полезных методов для работы с коллекцией элементов:
-
addOne
/ addMany
-
upsertOne
/ upsertMany
-
updateOne
/ updateMany
-
removeOne
/ removeMany
-
setAll
Их можно использовать в редьюсерах, например, для кейса
todoRemoved
.Для наглядности в демо-пример добавлены еще два экшена:
completedTodosRemoved
и listCleared
, которые используют методы адаптера.Селекторы
Кроме того, адаптер предоставляет два готовых селектора:
selectAll
и selectById
. Чтобы их получить, нужно вызвать метод adapter.getSelectors
. Так как этот метод работает с целым состоянием, нужно передать ему функцию для выделения нужно части состояния (в нашем случае state.todos
):const { selectAll, selectById } = todosAdapter.getSelectors(
function(state) {
return state.todos;
}
)
#redux #управлениесостоянием #примерыкода #паттерны #документация
Telegram
React Junior
Нормализация состояния
Во всех предыдущих примерах элементы хранились в store в виде массива. Но в больших проектах со сложным состоянием разумно использовать другую структуру данных - словарь с доступом по идентификатору (обычный JavaScript-объект).
Это…
Во всех предыдущих примерах элементы хранились в store в виде массива. Но в больших проектах со сложным состоянием разумно использовать другую структуру данных - словарь с доступом по идентификатору (обычный JavaScript-объект).
Это…
👍1🔥1
Фух, закончилось руководство Redux Fundamentals, разобрались в "низкоуровневых" концепциях redux. Думаю, теперь имеет смысл пройти Redux Essentials. Документация уверяет, что в этом руководстве больше внимания уделяется тому, как пишутся приложения "in real world".
Вероятно, какая-то информация там будет повторяться, так что, может, получится двигаться быстрее)
#redux #управлениесостоянием #документация
Вероятно, какая-то информация там будет повторяться, так что, может, получится двигаться быстрее)
#redux #управлениесостоянием #документация
redux.js.org
Redux Fundamentals, Part 1: Redux Overview | Redux
The official Fundamentals tutorial for Redux: learn the fundamentals of using Redux
👏4👍1