IT-KAMASUTRA о Reselect
У меня была какая-то необъяснимая сложность в понимании reselect, возможно, документация не очень правильно о нем говорит, ну либо причина гораздо более простая 🤓.
В общем, мне немного помогли разобраться три видео из курса по React с канала IT-KAMASUTRA.
81 - React JS - селекторы (reselect part 1)
82 - React JS - mapStateToProps (reselect часть 2)
83 - React JS - подключаем reselect (reselect часть 3)
Есть одна сложность - в курсе речь идет о старой версии redux, в которой не было хуков
#ссылки #redux #управлениесостоянием
У меня была какая-то необъяснимая сложность в понимании reselect, возможно, документация не очень правильно о нем говорит, ну либо причина гораздо более простая 🤓.
В общем, мне немного помогли разобраться три видео из курса по React с канала IT-KAMASUTRA.
81 - React JS - селекторы (reselect part 1)
82 - React JS - mapStateToProps (reselect часть 2)
83 - React JS - подключаем reselect (reselect часть 3)
Есть одна сложность - в курсе речь идет о старой версии redux, в которой не было хуков
useDispatch
и useSelect
. Там все было посложнее, использовался компонент высшего порядка (connect
), а глобальное состояние преобразовывалось в пропсы с помощью функций mapStateToProps
и mapDispatchToProps
. Это может вызвать трудности.#ссылки #redux #управлениесостоянием
YouTube
81 - React JS - селекторы (reselect part 1)
https://it-incubator.eu/education - человекоориентированное, честное и профессиональное обучение front-end и back-end разработке гарантированно до трудоустройства за фиксированную стоимость.
Поддержать меня можно на patreon https://www.patreon.com/itkamasutra…
Поддержать меня можно на patreon https://www.patreon.com/itkamasutra…
Reselect
Библиотека для создания мемоизированных селекторов от создателей redux: https://github.com/reduxjs/reselect
Позволяет вычислять "производное" состояние (в хранилище остаются только минимально необходимые данные, все остальное вычисляется).
Селекторы пересчитываются, только если изменяются их зависимости.
Кроме того, созданные селекторы можно использовать как "входные" для других селекторов.
createSelector (еще раз для полного осознания)
Для создания селектора используется функция
При вызове такого селектора под капотом сначала вызываются все входные селекторы (с полученными аргументами). Если они возвращают новое значение (отличное от того, которое было при предыдущем вызове), то вызывается выходной селектор. При этом аргументами для выходного селектора являются результаты работы входных селекторов. Если же все входные селекторы возвращают тот же результат, что и раньше, то выходной селектор не запускается, вместо этого просто возвращается значение, закешированное с прошлого раза.
Для сравнения результатов используется алгоритм строгого сравнения (
Размер кэша
По умолчанию размер кэша у такого селектора равен 1. Это значит, что хранится только одно предыдущее значение для каждого селектора.
Подробнее - в документации. Там есть утилитарные функции для мемоизации и ряд опций для настройки, а также FAQ с распространенными проблемами.
#redux #управлениесостоянием
Библиотека для создания мемоизированных селекторов от создателей redux: https://github.com/reduxjs/reselect
Позволяет вычислять "производное" состояние (в хранилище остаются только минимально необходимые данные, все остальное вычисляется).
Селекторы пересчитываются, только если изменяются их зависимости.
Кроме того, созданные селекторы можно использовать как "входные" для других селекторов.
createSelector (еще раз для полного осознания)
Для создания селектора используется функция
createSelector
. Она принимает один или несколько "входных" селекторов (в виде массива или просто отдельными аргументами) и один "выходной". Еще может принимать объект с настройками.При вызове такого селектора под капотом сначала вызываются все входные селекторы (с полученными аргументами). Если они возвращают новое значение (отличное от того, которое было при предыдущем вызове), то вызывается выходной селектор. При этом аргументами для выходного селектора являются результаты работы входных селекторов. Если же все входные селекторы возвращают тот же результат, что и раньше, то выходной селектор не запускается, вместо этого просто возвращается значение, закешированное с прошлого раза.
Для сравнения результатов используется алгоритм строгого сравнения (
===
, сравнение по ссылке). Размер кэша
По умолчанию размер кэша у такого селектора равен 1. Это значит, что хранится только одно предыдущее значение для каждого селектора.
Подробнее - в документации. Там есть утилитарные функции для мемоизации и ряд опций для настройки, а также FAQ с распространенными проблемами.
#redux #управлениесостоянием
👏1
Валидация форм в 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