изначально опубликовано на almoullim.com
Redux - это библиотека JavaScript с открытым исходным кодом для управления состоянием приложения. Redux Toolkit - это самоуверенный набор инструментов для разработки Redux с включенными батареями.
Redux Toolkit предоставляет набор инструментов, которые помогают нам реализовать Redux более простым (хотя и самоуверенным) способом.
Внедрение Redux в проект осуществляется путем создания действий и редукторов. Действия описывают, какие изменения мы хотим произойти с хранилищем, и редукторы выполняют эти изменения, а затем хранилище Redux уведомляет наши компоненты об изменении значений хранилища, и они повторно визуализируются с обновленными значениями.
Действия - это просто объект JS, содержащий type
действие, которое мы хотим выполнить, и payload
, который представляет собой данные, необходимые для этого действия.
например, здесь наше действие состоит в том, чтобы добавить задачу, и мы предоставляем данные в полезной нагрузке с информацией о ней:
{ type: 'ADD_TODO', payload: { name: 'Buy Groceries', }, }
и чтобы еще больше упростить отправку действий, мы можем создать функцию, которая возвращает нам полный объект:
function addTodo(text) { return { type: 'ADD_TODO', payload: { name: text } } }
Редукторы - это функция, которая принимает предыдущее состояние, действие, которое мы хотим выполнить, и возвращает новое состояние после выполнения этого действия, редуктор может выполнять более одного действия, поэтому мы используем оператор switch-case для знать, какую операцию выполнить, зная, какое действие было отправлено. мы также можем передать начальное состояние нашим редукторам.
например, чтобы добавить задачу:
const initialState = [ { name: 'Wash the car'. completed: false }, { name: 'Wash the clothes', completed: false } ] function todosReducer(state = initialState, action) { switch (action.type) { case ADD_TODO: return [...state.todos, {...action.payload, completed: false}] default: return state } }
В Redux состояние не должно изменяться, мы всегда должны возвращать новое состояние, поэтому мы не делали state.push(action.payload)
, а вместо этого создали новый массив, используемый для оператора распространения, чтобы скопировать все предыдущие задачи в новый массив и добавить наши новые todo, затем вернул этот новый массив, который является нашим новым состоянием.
После этого мы можем создать больше редукторов и объединить их с помощью функции combineReducers
из redux в один корневой редуктор и, наконец, передать его в хранилище:
const rootReducer = combineReducers({ todosReducer }) const store = createStore(reducer)
затем мы можем получить текущее состояние магазина, подписаться на магазин и отправлять действия, как нам нравится:
function onStateChange() { console.log(store.getState()) } store.subscribe(onStateChange) const newTodo = {name: 'Buy Groceries'} store.dispatch({ type: 'ADD_TODO', payload: newTodo }) // or use the previously created function store.dispatch(addTodo('Buy Groceries'))
Теперь в Redux есть много лишних шаблонов, и Redux Toolkit забирает у нас большую часть этого шаблона.
Среди других функций он предоставляет нам функцию с именем createSlice
, которая обрабатывает логику создания типов действий, создателей действий, редукторов, switch-case / if-else, возврата состояния в случае по умолчанию, возврата нового состояния (дайте нам скопированный состояние, позволяющее нам изменять его напрямую и автоматически возвращать его как новый). Он также предоставляет функцию configureStore
, которая объединяет редукторы, включает Redux DevTools и добавляет redux-thunk (для асинхронных действий):
Таким образом, мы можем переписать предыдущий код с помощью Redux Toolkit следующим образом:
const todosSlice = createSlice({ name: 'todos', initialState: [ { name: 'Wash the car', completed: false }, { name: 'Wash the clothes', completed: false }, ], reducers: { addTodo: (state, action) { state.push(action.payload) } }, }) const store = configureStore({ reducer: { todos: todosSlice } })
Итак, давайте рассмотрим весь предыдущий код, который мы сделали без Redux Toolkit, и создадим остальные действия, пока мы на нем:
import { createStore, combineReducers } from 'redux' const ADD_TODO = 'ADD_TODO' const REMOVE_TODO = 'REMOVE_TODO' const TOGGLE_TODO = 'TOGGLE_TODO' function addTodo(text) { return { type: 'ADD_TODO', payload: { name: text } } } function removeTodo(index) { return { type: 'REMOVE_TODO', payload: { index } } } function toggleTodo(index) { return { type: 'TOGGLE_TODO', payload: { index } } } const initialState = [ { name: 'Wash the car', completed: false }, { name: 'Wash the clothes', completed: false }, ] function todosReducer(state = initialState, action) { switch (action.type) { case ADD_TODO: return [...state, action.payload] case REMOVE_TODO: return state.filter((todo, index) => { if (index === action.payload.index) { return false } return true }) case TOGGLE_TODO: return state.map((todo, index) => { if (index === action.payload.index) { return { ...todo, completed: !todo.completed, } } return todo }) default: return state } } const rootReducer = combineReducers({ todosReducer }) const store = createStore(reducer) export default store export { addTodo, removeTodo, toggleTodo } // To test that the store is working function onStateChange() { console.log(store.getState()) } store.subscribe(onStateChange) store.dispatch(addTodo('Buy Groceries'))
А теперь перепишем его с помощью Redux Toolkit:
import { createSlice, configureStore } from '@reduxjs/toolkit' const todosSlice = createSlice({ name: 'todos', initialState: [ { name: 'Wash the car', completed: false }, { name: 'Wash the clothes', completed: false }, ], reducers: { addTodo(state, action) { state.push(action.payload) }, removeTodo(state, action) { state.splice(action.payload.index, 1) }, toggleTodo(state, action) { state[index].completed = !state[index].completed }, }, }) const store = configureStore({ reducer: { todos: todosSlice } }) export default store export const { addTodo, removeTodo, toggleTodo } = todosSlice.actions // To test that the store is working function onStateChange() { console.log(store.getState()) } store.subscribe(onStateChange) store.dispatch(todosSlice.actions.addTodo('Buy Groceries'))
Думайте о createSlice
срезах как о модулях хранилища, и вы можете создать столько модулей (срезов), сколько вам нужно, а затем добавить их к объекту, который вы передаете в configureStore
Теперь вы можете приступить к интеграции вашего магазина Redux с React с помощью React Redux, обернув ваше приложение магазином Provider
:
import React from 'react' import ReactDOM from 'react-dom' import { Provider } from 'react-redux' import store from './store' import App from './App' const rootElement = document.getElementById('root') ReactDOM.render( <Provider store={store}> <App /> </Provider>, rootElement )
И соединяем ваши компоненты с connect
:
import { connect } from 'react-redux' import { addTodo, removeTodo, toggleTodo } from './store' // const Todos = ... const mapStateToProps = (state /*, ownProps*/) => ({ todos: state.todos, }) // "object shorthand" form of mapDispatch with action creators const mapDispatchToProps = { addTodo, removeTodo, toggleTodo } export default connect( mapStateToProps, mapDispatchToProps )(Todos)
Не забудьте npm install ...
(redux
входит в @reduxjs/toolkit
):
npm install @reduxjs/toolkit react-redux