redux toolkit (RTK)
简介
Redux Toolkit(简称 RTK)是 Redux 官方推出的工具集,旨在解决传统 Redux 开发中的一些痛点问题。
相比直接使用 react-redux ,它使用更为简便,主要解决了以下诸多核心问题:
1. 简化样板代码;
2. 简化不可变状态的更新;
3. 简化 Store 配置;
4. 简化异步逻辑处理。
详解
1. 简化样板代码
传统 Redux 开发需要手动编写:
- Action Type 常量(如
const ADD_TODO = 'ADD_TODO'
)
// actionTypes.js
export const ADD_TODO = 'todos/ADD_TODO'; // 新增待办
export const TOGGLE_TODO = 'todos/TOGGLE_TODO'; // 切换待办状态
export const DELETE_TODO = 'todos/DELETE_TODO'; // 删除待办
- Action Creator 函数(如
const addTodo = (text) => ({ type: ADD_TODO, payload: text })
)
// actions.js
import { ADD_TODO, TOGGLE_TODO, DELETE_TODO } from './actionTypes';// 新增待办的 Action Creator(需手动传递 text 参数,绑定 type)
export const addTodo = (text) => {return {type: ADD_TODO, // 手动关联第一步定义的常量payload: text, // 携带数据(需手动命名 payload,无统一约定)};
};// 切换待办状态的 Action Creator(需手动传递 id 参数)
export const toggleTodo = (id) => {return {type: TOGGLE_TODO,payload: id,};
};// 删除待办的 Action Creator(需手动传递 id 参数)
export const deleteTodo = (id) => {return {type: DELETE_TODO,payload: id,};
};
- Reducer 中的 switch-case 逻辑(处理不同 Action Type)
// reducer.js
import { ADD_TODO, TOGGLE_TODO, DELETE_TODO } from './actionTypes';// 初始状态(需手动定义)
const initialState = [];// Reducer 函数(手动编写 switch-case 匹配每个 Action Type)
const todoReducer = (state = initialState, action) => {switch (action.type) {// 处理“新增待办”:手动实现不可变更新(...state 复制旧状态)case ADD_TODO:return [...state, // 复制原有待办列表{id: Date.now(), // 手动生成 idtext: action.payload, // 从 Action 中取数据done: false, // 默认未完成},];// 处理“切换待办状态”:手动遍历数组,返回新对象case TOGGLE_TODO:return state.map((todo) =>todo.id === action.payload ? { ...todo, done: !todo.done } // 复制单个 todo 并修改状态: todo // 未匹配的 todo 保持不变);// 处理“删除待办”:手动过滤数组,返回新数组case DELETE_TODO:return state.filter((todo) => todo.id !== action.payload);// 默认返回原状态(必须写,否则初始状态会错)default:return state;}
};export default todoReducer;
RTK 解决方案:
通过 createSlice
自动生成 Action Type、Action Creator 和 Reducer,大幅减少代码量。
// Redux Toolkit 写法
import { createSlice } from '@reduxjs/toolkit';const todoSlice = createSlice({name: 'todos',initialState: [],reducers: {addTodo: (state, action) => {// 直接修改状态(内部通过 Immer 实现不可变更新)state.push({ id: Date.now(), text: action.payload, done: false });},toggleTodo: (state, action) => {const todo = state.find(t => t.id === action.payload);if (todo) todo.done = !todo.done;}}
});// 自动生成 Action Creator
export const { addTodo, toggleTodo } = todoSlice.actions;
// 自动生成 Reducer
export default todoSlice.reducer;
2. 简化不可变状态更新
传统 Redux 写法:
通过不可变方式更新状态(如 return { ...state, ...newState }
),手动编写容易出错且繁琐。
RTK 解决方案:
内置 Immer 库,允许在 Reducer 中直接 “修改” 状态(如 state.push(...)
),Immer 会自动将其转换为不可变更新,既直观又不易出错。
3. 简化 Store 配置
传统 Redux 需要手动配置:
createStore
- 中间件(如
applyMiddleware(thunk)
) - 开发者工具(
window.__REDUX_DEVTOOLS_EXTENSION__
)
// store.js
import { createStore, applyMiddleware, combineReducers } from 'redux';
import thunk from 'redux-thunk'; // 需手动安装(npm install redux-thunk)
import todoReducer from './reducer';// 若有多个 Reducer,需手动合并(combineReducers)
const rootReducer = combineReducers({todos: todoReducer, // 命名状态键(todos 对应 todoReducer 的状态)
});// 手动配置中间件和 DevTools(这段代码几乎每个项目都重复)
const store = createStore(rootReducer,// 开启 Redux DevTools(需手动写条件判断,兼容生产环境)window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),applyMiddleware(thunk) // 手动集成 Thunk 中间件
);export default store;
RTK 解决方案:
configureStore
函数一站式配置 Store,默认集成 Redux Thunk、Redux DevTools,并简化中间件设置:
// Redux Toolkit 写法
import { configureStore } from '@reduxjs/toolkit';
import todoReducer from './todoSlice';export const store = configureStore({reducer: {todos: todoReducer}
});
4. 简化异步逻辑处理
传统 Redux 处理异步(如 API 请求):
- 手动编写异步 Action(依赖
redux-thunk
) - 定义加载中、成功、失败等 Action Type
- 在 Reducer 中处理多种状态(
isLoading
、error
等)
RTK 解决方案:
createAsyncThunk
自动生成异步 Action,结合 createSlice
的 extraReducers
处理异步状态:
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import { fetchTodos } from './api';// 自动生成 pending/fulfilled/rejected 三种 Action
export const getTodos = createAsyncThunk('todos/fetchTodos',async () => {const response = await fetchTodos();return response.data;}
);const todoSlice = createSlice({name: 'todos',initialState: { items: [], isLoading: false, error: null },reducers: {},extraReducers: (builder) => {builder.addCase(getTodos.pending, (state) => {state.isLoading = true;}).addCase(getTodos.fulfilled, (state, action) => {state.isLoading = false;state.items = action.payload;}).addCase(getTodos.rejected, (state, action) => {state.isLoading = false;state.error = action.error.message;});}
});
总结
Redux Toolkit 并非替代 Redux 或 React-Redux,而是在它们的基础上封装了一套最佳实践,解决了传统开发中的样板代码冗余、不可变更新繁琐、配置复杂、异步逻辑处理麻烦等问题,让 Redux 开发更高效、更易维护。
代码示例
[代码]:
index.js:
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './slices/counterSlice'
import userReducer from './slices/userSlice'export const store = configureStore({reducer: {counter: counterReducer,user: userReducer},
});
slices/userSlice.js:
import { createSlice } from '@reduxjs/toolkit'const userSlice = createSlice({name: 'user',initialState: {name: '张三',age: 18,},reducers: {setName(state, action) {state.name = action.payload},setAge(state, action) {state.age = action.payload},},
})
export const { setName, setAge } = userSlice.actions
export default userSlice.reducer
slices/counterSlice.js:
import { createSlice } from '@reduxjs/toolkit';const initialState = {counter: 0,
};const counterSlice = createSlice({name: 'counter',initialState,reducers: {increment: (state) => {state.counter += 1;},decrement: (state) => {state.counter -= 1;},incrementByAmount: (state, action) => {state.counter += action.payload;},},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
组件中具体调用:
import React, { useEffect, useLayoutEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'// 1. react-redux
const ReactRedux = () => { let { age } = useSelector((state: any) => state.user)const dispatch = useDispatch()const changeUserAgeInStore = () => {// 改变状态管理器中的 user.agedispatch({type: 'user/setAge',payload: ++age})}return (<button onClick={ changeUserAgeInStore }>改变状态管理器中的 user.age</button>)
}