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
Create React App + Redux
CRA предоставляет готовый официальный шаблон с уже подключенным Redux Toolkit. Для этого нужно использовать опцию
Можно даже с typescript, если вы уже его используете (я пока нет):
UPD: да, и конечно тут уже есть интеграция с Redux DevTools
#redux #управлениесостоянием #инструменты
CRA предоставляет готовый официальный шаблон с уже подключенным Redux Toolkit. Для этого нужно использовать опцию
--template
:npx create-react-app my-app --template reduxТо есть ничего дополнительно подключать не нужно, эта функциональность уже есть в create-react-app из коробки.
Можно даже с typescript, если вы уже его используете (я пока нет):
npx create-react-app my-app --template redux-typescriptВ созданном проекте уже есть папка
features
и даже готовый пример features/counter
с примерами использования методов createSlice, createAsyncThunk и configureStore.UPD: да, и конечно тут уже есть интеграция с Redux DevTools
#redux #управлениесостоянием #инструменты
👍6
После довольно долгого перерыва будет полезно быстро вспомнить, что мы уже знаем о Redux Toolkit.
Сделаем это на примере простого демо-проекта Счетчика из официальной документации Redux:
🗂 Разделение на фичи
Хорошая практика - делить общее глобальное хранилище на несколько частей, соответствующих логическому разделению функционала приложения. Например, в одном из предыдущих проектов (TODO-лист) мы разделили хранилище на две части:
А в Счетчике фича всего одна - собственно логика счетчика, это тоже нормально.
Документация рекомендует разносить такие фичи по отдельным папкам - в каждой собственный слайс (редьюсер + экшены) и свои компоненты.
🔪 Создание слайсов
Слайс создается с помощью функции createSlice из Redux Toolkit.
Пример для счетчика в файле counterSlice.js.
В Redux Toolkit мы уже не мыслим отдельными понятиями редьюсер и экшены, тут у нас все сразу вместе (и если подумать, это действительно удобнее). По сути мы перечисляем типы экшенов и для каждого указываем логику обработки - такой маленький редьюсер для одного экшена.
При этом под капотом генерируются action creators. Они доступны в поле
⏳ Thunks
Для thunk creators вообще-то существует отдельная функция createAsyncThunk, но в этом вводном демо-проекте авторы используют привычный способ - просто создают функцию
Но при желании с помощью
🗄 Создание хранилища
Хранилище создается в файле app/store.js.
Здесь импортируются все необходимые редьюсеры (тут только один) и объединяются с помощью функции configureStore. Под капотом тут подключаются разные полезные миддлвары, включая redux-thunk и redux-devtools.
🧲 Подключение хранилища к приложению
За "мост" между Redux и React отвечает пакет react-redux, у которого есть компонент
✋ Доступ к хранилищу из компонентов
Для получения данных из хранилища используются функции-селекторы, которые знают, как добраться до нужных данных. Они находятся в том же файле, что и слайс, максимально близко к данным, собственно.
А в компонентах мы используем хук
Пример в компоненте Counter.
Redux Toolkit в целом много оптимизует под капотом, но селекторы можно оптимизировать дополнительно с помощью функции createSelector (в Счетчике она не используется).
🪃 Отправка экшенов и thunks
Для отправки экшенов используем хук
#документация #управлениесостоянием #redux #примерыкода
Сделаем это на примере простого демо-проекта Счетчика из официальной документации Redux:
🗂 Разделение на фичи
Хорошая практика - делить общее глобальное хранилище на несколько частей, соответствующих логическому разделению функционала приложения. Например, в одном из предыдущих проектов (TODO-лист) мы разделили хранилище на две части:
todos
и filters
. В первой были все операции, связанные со списком (CRUD), во втором - вся логика фильтрации. Они частично пересекаются (отфильтрованный список), но в целом это две разные функциональности.А в Счетчике фича всего одна - собственно логика счетчика, это тоже нормально.
Документация рекомендует разносить такие фичи по отдельным папкам - в каждой собственный слайс (редьюсер + экшены) и свои компоненты.
🔪 Создание слайсов
Слайс создается с помощью функции createSlice из Redux Toolkit.
Пример для счетчика в файле counterSlice.js.
В Redux Toolkit мы уже не мыслим отдельными понятиями редьюсер и экшены, тут у нас все сразу вместе (и если подумать, это действительно удобнее). По сути мы перечисляем типы экшенов и для каждого указываем логику обработки - такой маленький редьюсер для одного экшена.
При этом под капотом генерируются action creators. Они доступны в поле
counterSlice.actions
.⏳ Thunks
Для thunk creators вообще-то существует отдельная функция createAsyncThunk, но в этом вводном демо-проекте авторы используют привычный способ - просто создают функцию
incrementAsync
, которая возвращает thunk, без дополнительной интеграции в слайс. Но при желании с помощью
createAsyncThunk
мы можем подключиться к событиям "начала" и "конца" выполнения асинхронной операции.🗄 Создание хранилища
Хранилище создается в файле app/store.js.
Здесь импортируются все необходимые редьюсеры (тут только один) и объединяются с помощью функции configureStore. Под капотом тут подключаются разные полезные миддлвары, включая redux-thunk и redux-devtools.
🧲 Подключение хранилища к приложению
За "мост" между Redux и React отвечает пакет react-redux, у которого есть компонент
Provider
, который обеспечивает проброс хранилища до всех заинтересованных компонентов (используется контекст).✋ Доступ к хранилищу из компонентов
Для получения данных из хранилища используются функции-селекторы, которые знают, как добраться до нужных данных. Они находятся в том же файле, что и слайс, максимально близко к данным, собственно.
А в компонентах мы используем хук
useSelector
(из пакета react-redux), которому передаем нужный селектор. Пример в компоненте Counter.
Redux Toolkit в целом много оптимизует под капотом, но селекторы можно оптимизировать дополнительно с помощью функции createSelector (в Счетчике она не используется).
🪃 Отправка экшенов и thunks
Для отправки экшенов используем хук
useDispatch
(из пакета react-redux). По сути это обращение к методу store.dispatch
. В него передаем или объект экшена, или thunk.#документация #управлениесостоянием #redux #примерыкода
CodeSandbox
redux-essentials-counter-example - CodeSandbox
redux-essentials-counter-example using @reduxjs/toolkit, @testing-library/jest-dom, @testing-library/react, @testing-library/user-event, react, react-dom, react-redux, react-scripts
👍2
Новый проект
Наконец-то начинаю работу над новым проектом в рамках руководства Redux Essentials, которое должно научить меня лучшим практикам Redux.
Готовый проект выглядит так: https://codesandbox.io/s/github/reduxjs/redux-essentials-example-app/tree/tutorial-steps
Тут три вкладки:
- Posts - лента постов + форма для создания новой записи. Отсюда можно перейти на страницу отдельного поста и отредактировать его.
- Users - список пользователей. У каждого пользователя есть своя страница со списком его записей.
- Notifications - лента уведомлений, которую можно обновлять.
В демо-версии кое-что не работает или работает через раз, но это не имеет значения, все равно будем все переписывать с нуля :)
Первый разбор
Очевидно, что в проекте три основных фичи: посты, пользователи и уведомления. Для каждой из них, вероятно, нужно будет создать отдельный слайс.
Все данные приходят с сервера, а изменения синхронизируются с ним, значит, придется использовать какой-то api, слать запросы и асинхронно их обрабатывать. Тут нам вероятно понадобятся thunks. Спойлер: для api используется, очевидно, RTK Query, вот и повод с ним познакомиться.
Посты
Есть форма для добавления поста, ее состояние будет храниться локально. При отправке будем отправлять thunk. Понадобится также отслеживать начало и конец выполнения, чтобы отображать состояние отправки.
Есть список постов, а у постов есть реакции, скорее всего, их следует хранить в данных самого поста, так как они несамостоятельны.
Можно просматривать каждый пост, значит, нужно получать его данные по какому-то идентификатору.
Пост можно отредактировать, тут все по аналогии с формой добавления.
Пользователи
Есть список пользователей (имя и ссылка на отдельную страницу), а также список записей, созданных пользователем.
Уведомления
Опять же обычный список. Плюс есть кнопка Refresh Notifications, которая подгружает новую порцию элементов. Их нужно будет добавить в существующий массив.
Свежие уведомления выделяются фоном.
Начало работы
Итак, первый взгляд бросили. Теперь создаем новый проект и начинаем работать.
Для этого либо используем CRA с шаблоном redux:
#redux #управлениесостоянием #документация
Наконец-то начинаю работу над новым проектом в рамках руководства Redux Essentials, которое должно научить меня лучшим практикам Redux.
Готовый проект выглядит так: https://codesandbox.io/s/github/reduxjs/redux-essentials-example-app/tree/tutorial-steps
Тут три вкладки:
- Posts - лента постов + форма для создания новой записи. Отсюда можно перейти на страницу отдельного поста и отредактировать его.
- Users - список пользователей. У каждого пользователя есть своя страница со списком его записей.
- Notifications - лента уведомлений, которую можно обновлять.
В демо-версии кое-что не работает или работает через раз, но это не имеет значения, все равно будем все переписывать с нуля :)
Первый разбор
Очевидно, что в проекте три основных фичи: посты, пользователи и уведомления. Для каждой из них, вероятно, нужно будет создать отдельный слайс.
Все данные приходят с сервера, а изменения синхронизируются с ним, значит, придется использовать какой-то api, слать запросы и асинхронно их обрабатывать. Тут нам вероятно понадобятся thunks. Спойлер: для api используется, очевидно, RTK Query, вот и повод с ним познакомиться.
Посты
Есть форма для добавления поста, ее состояние будет храниться локально. При отправке будем отправлять thunk. Понадобится также отслеживать начало и конец выполнения, чтобы отображать состояние отправки.
Есть список постов, а у постов есть реакции, скорее всего, их следует хранить в данных самого поста, так как они несамостоятельны.
Можно просматривать каждый пост, значит, нужно получать его данные по какому-то идентификатору.
Пост можно отредактировать, тут все по аналогии с формой добавления.
Пользователи
Есть список пользователей (имя и ссылка на отдельную страницу), а также список записей, созданных пользователем.
Уведомления
Опять же обычный список. Плюс есть кнопка Refresh Notifications, которая подгружает новую порцию элементов. Их нужно будет добавить в существующий массив.
Свежие уведомления выделяются фоном.
Начало работы
Итак, первый взгляд бросили. Теперь создаем новый проект и начинаем работать.
Для этого либо используем CRA с шаблоном redux:
npx create-react-app my-app --template reduxЛибо подключаем по отдельности react-redux и @reduxjs/toolkit.
#redux #управлениесостоянием #документация
CodeSandbox
redux-essentials-example - CodeSandbox
redux-essentials-example using @faker-js/faker, @mswjs/data, @reduxjs/toolkit, classnames, date-fns, mock-socket, msw, react, react-dom
👍2🔥1
Redux Essentials 1. Лента статей
Начинаем с вывода списка статей. Тут все очень просто, мы такое уже не раз делали.
https://codesandbox.io/s/redux-essentials-posts-react-junior-6cyh18?file=/src/App.js
Как и предполагалось, создаем отдельную папку features/posts, а в ней файл postsSlice.js. Используем функцию
Сразу здесь же создаем селектор
В файле app/store.js создаем хранилище (функция
В файле App.js подготавливаем структуру приложения. У нас будет много страниц, поэтому подключаем react-router-dom. Пока роут только один - индексный. Тут будет выводиться лента постов, а затем еще и форма создания поста.
Компонент ленты постов - features/posts/PostsList.js. Чтобы получить список, используем хук
#redux #управлениесостоянием #документация #примерыкода
Начинаем с вывода списка статей. Тут все очень просто, мы такое уже не раз делали.
https://codesandbox.io/s/redux-essentials-posts-react-junior-6cyh18?file=/src/App.js
Как и предполагалось, создаем отдельную папку features/posts, а в ней файл postsSlice.js. Используем функцию
createSlice
- пока без всяких редьюсеров, только с начальным состоянием. Сразу здесь же создаем селектор
getPosts
для получения списка статей.В файле app/store.js создаем хранилище (функция
createStore
), подключаем созданный слайс. В файле App.js подготавливаем структуру приложения. У нас будет много страниц, поэтому подключаем react-router-dom. Пока роут только один - индексный. Тут будет выводиться лента постов, а затем еще и форма создания поста.
Компонент ленты постов - features/posts/PostsList.js. Чтобы получить список, используем хук
useSelector
.#redux #управлениесостоянием #документация #примерыкода
CodeSandbox
Redux Essentials. Posts. React Junior - CodeSandbox
Redux Essentials. Posts. React Junior by furrycat.web using @reduxjs/toolkit, react, react-dom, react-redux, react-router-dom, react-scripts, styled-components
👍4
Redux Essentials 2. Добавление статей
Важно: Появились проблемы с обновленным React 18, поэтому пока переключаюсь на версию 17.0.2.
https://codesandbox.io/s/redux-essentials-add-post-react-junior-5wmcg3?file=/src/App.js
Здесь все тоже знакомо.
Для внесения изменений в состояние нам потребуется экшен -
Важно: Внутри функции
Redux Toolkit автоматически генерирует нам action creator с таким же названием
Добавляем форму создания статьи - компонент features/posts/AddPostForm.js. Выводим ее в компоненте
Все состояние формы хранится внутри компонента. При отправке вызываем метод
#redux #управлениесостоянием #документация #примерыкода
Важно: Появились проблемы с обновленным React 18, поэтому пока переключаюсь на версию 17.0.2.
https://codesandbox.io/s/redux-essentials-add-post-react-junior-5wmcg3?file=/src/App.js
Здесь все тоже знакомо.
Для внесения изменений в состояние нам потребуется экшен -
postAdded
. Добавляем его обработчик в postsSlice
, в секцию reducers
. При возникновении экшена просто пушим его данные в state
.Важно: Внутри функции
createSlice
допустимо мутировать состояние, так как оно специально обрабатывается. Redux Toolkit автоматически генерирует нам action creator с таким же названием
postAdded
, который можно забрать из postSlice.actions
.Добавляем форму создания статьи - компонент features/posts/AddPostForm.js. Выводим ее в компоненте
App
.Все состояние формы хранится внутри компонента. При отправке вызываем метод
dispatch
, чтобы отправить экшен в хранилище (берем из хука useDispatch
). Сам экшен генерируем с помощью креатора из предыдущего абзаца.#redux #управлениесостоянием #документация #примерыкода
CodeSandbox
Redux Essentials. Add post. React Junior - CodeSandbox
Redux Essentials. Add post. React Junior by furrycat.web using @reduxjs/toolkit, react, react-dom, react-redux, react-router-dom, react-scripts, styled-components
👍4
Кстати, в пакете @reduxjs/toolkit есть функция
#redux #документация
nanoid
для генерации уникальных идентификаторов.import { nanoid } from "@reduxjs/toolkit";
#redux #документация
👍5