为什么 Redux Toolkit 是当今使用 Redux 的方式
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
什么是 Redux Toolkit?
Redux Toolkit(简称 RTK)是我们官方推荐的 Redux 逻辑编写方式。@reduxjs/toolkit 包封装了核心的 redux 包,包含我们认为构建 Redux 应用必需的 API 方法和常见依赖项。Redux Toolkit 内置了推荐的最佳实践,简化了大多数 Redux 任务,避免了常见错误,让编写 Redux 应用更加容易。
如果你现在正在编写_任何_ Redux 逻辑,你_应该_使用 Redux Toolkit 来编写代码!
RTK 提供的工具可简化多种常见场景,包括 store 配置、 创建 reducer 和编写不可变更新逻辑, 甚至能一次性创建完整的状态切片。
无论你是刚接触 Redux 的新用户正在创建第一个项目,还是希望简化现有应用的经验丰富的开发者, Redux Toolkit 都能帮助你优化 Redux 代码。
查看以下页面学习如何结合 Redux Toolkit 使用"现代 Redux":
- 《Redux 精髓》教程:使用 Redux Toolkit 教授"如何正确使用 Redux"构建真实应用
- Redux 基础教程第 8 篇:使用 Redux Toolkit 的现代 Redux:展示如何将教程前期底层示例转换为现代 Redux Toolkit 实现
- 使用 Redux:迁移到现代 Redux:讲解各类传统 Redux 逻辑如何迁移到现代等效实现
Redux Toolkit 与 Redux 核心的区别
什么是"Redux"?
首先要明确:"Redux 是什么?"
Redux 本质上是:
-
包含"全局"状态的单一 store
-
当应用发生事件时,向 store 派发普通对象格式的 action
-
纯 reducer 函数处理这些 action 并返回不可变更新的状态
虽然非必须,但Redux 代码通常还包含:
-
生成 action 对象的 action creator
-
支持副作用的 middleware
-
包含同步/异步逻辑和副作用的 thunk 函数
-
支持按 ID 查找的归一化状态
-
使用 Reselect 库的 memoized selector 函数优化派生数据
-
查看 action 历史和状态变更的 Redux DevTools 扩展
-
为 action、状态等提供的 TypeScript 类型
此外,Redux 通常配合 React-Redux 库使用,让 React 组件能与 Redux store 交互。
Redux 核心的作用是什么?
Redux 核心是个非常精简且刻意保持中立的库,仅提供少量 API 基础功能:
-
createStore实际创建 Redux store -
combineReducers合并多个切片 reducer -
applyMiddleware组合多个 middleware -
compose组合多个 store 增强器
除此之外,应用中所有其他 Redux 相关逻辑都需要完全自行实现。
好消息是这意味着 Redux 可以 用多种方式使用。坏消息是没有辅助工具来简化任何代码编写。
例如,reducer 函数本质上就是一个普通函数。在使用 Redux Toolkit 之前,你通常会使用 switch 语句和手动更新来编写 reducer。同时还需要手写 action 创建函数和 action 类型常量:
const ADD_TODO = 'ADD_TODO'
const TODO_TOGGLED = 'TODO_TOGGLED'
export const addTodo = text => ({
type: ADD_TODO,
payload: { text, id: nanoid() }
})
export const todoToggled = id => ({
type: TODO_TOGGLED,
payload: id
})
export const todosReducer = (state = [], action) => {
switch (action.type) {
case ADD_TODO:
return state.concat({
id: action.payload.id,
text: action.payload.text,
completed: false
})
case TODO_TOGGLED:
return state.map(todo => {
if (todo.id !== action.payload) return todo
return {
...todo,
completed: !todo.completed
}
})
default:
return state
}
}
这些代码并不直接依赖于 redux 核心库的任何 API。但编写这些代码需要大量工作。不可变更新需要大量手写的对象展开和数组操作,非常容易出错并意外变更状态(这始终是 Redux 错误的头号原因!)。虽然并非强制要求,但通常会将单个功能的代码分散到多个文件中,例如 actions/todos.js、constants/todos.js 和 reducers/todos.js。
此外,store 配置通常需要一系列步骤来添加常用中间件(如 thunks)并启用 Redux DevTools 扩展支持——尽管这些几乎是每个 Redux 应用都会使用的标准工具。
Redux Toolkit 的作用是什么?
虽然这些模式最初出现在 Redux 文档中,但它们不幸地需要编写大量冗长重复的代码。这些样板代码的大部分对于使用 Redux 来说并非必需。更重要的是,样板代码反而增加了出错的可能性。
我们专门创建了 Redux Toolkit,旨在消除手写 Redux 逻辑中的"样板代码",防止常见错误,并提供简化标准 Redux 任务的 API。
Redux Toolkit 从两个关键 API 开始,它们简化了每个 Redux 应用中最常见的操作:
-
configureStore:通过单次函数调用即可配置完善的 Redux store,包括组合 reducer、添加 thunk 中间件和设置 Redux DevTools 集成。它比createStore更易配置,因为它接受命名选项参数。 -
createSlice:让你使用 Immer 库 编写 reducer,支持通过state.value = 123这类"突变式" JavaScript 语法进行不可变更新,无需手动展开。它还会自动为每个 reducer 生成 action 创建函数,并根据 reducer 名称内部生成 action 类型字符串。最后,它与 TypeScript 完美兼容。
这意味着你编写的代码可以大幅简化。例如,同样的 todos reducer 可以这样写:
import { createSlice } from '@reduxjs/toolkit'
const todosSlice = createSlice({
name: 'todos',
initialState: [],
reducers: {
todoAdded(state, action) {
state.push({
id: action.payload.id,
text: action.payload.text,
completed: false
})
},
todoToggled(state, action) {
const todo = state.find(todo => todo.id === action.payload)
todo.completed = !todo.completed
}
}
})
export const { todoAdded, todoToggled } = todosSlice.actions
export default todosSlice.reducer
所有 action 创建函数和 action 类型都会自动生成,reducer 代码更短且更易理解。每个场景中实际更新的内容也变得更加清晰。
通过 configureStore,store 配置可以简化为:
import { configureStore } from '@reduxjs/toolkit'
import todosReducer from '../features/todos/todosSlice'
import filtersReducer from '../features/filters/filtersSlice'
export const store = configureStore({
reducer: {
todos: todosReducer,
filters: filtersReducer
}
})
请注意仅需一次 configureStore 调用,就能自动完成所有通常需要手动完成的工作:
-
切片 reducer 已自动传递给
combineReducers() -
已自动添加
redux-thunk中间件 -
已添加开发模式中间件来捕获意外变更
-
已自动设置 Redux DevTools 扩展
-
中间件和 DevTools 增强器已组合并添加到 store
同时,configureStore 提供了选项让用户修改任何默认行为(例如关闭 thunks 并添加 sagas,或在生产环境禁用 DevTools)
除此之外,Redux Toolkit 还包含其他用于常见 Redux 任务的 API:
-
createAsyncThunk:抽象了"在异步请求前后分发 action"的标准模式 -
createEntityAdapter:为规范化状态的 CRUD 操作提供预构建的 reducer 和选择器 -
createSelector:重新导出了 Reselect 库的标准 API,用于创建记忆化选择器 -
createListenerMiddleware:用于响应分发 action 执行逻辑的副作用中间件
最后,RTK 包还包含"RTK Query"——这是 Redux 应用的全功能数据获取和缓存解决方案,作为独立的可选入口点 @reduxjs/toolkit/query 提供。它允许你定义端点(REST、GraphQL 或任何异步函数),并生成完全管理数据获取、更新加载状态和缓存结果的 reducer 和中间件。同时自动生成可在组件中使用的 React hooks 来获取数据,例如 const { data, isFetching } = useGetPokemonQuery('pikachu')
这些 API 都是完全可选的,针对特定场景设计,你可以自由选择在应用中实际使用哪些 API。但强烈建议使用这些工具来完成相应任务。
请注意 Redux Toolkit 仍然是"Redux"! 它依然使用单一 store,通过派发 action 对象更新状态,reducer 以不可变方式更新状态,同时支持编写 thunk 处理异步逻辑、管理规范化状态、使用 TypeScript 进行类型检查以及使用开发者工具。区别在于实现相同效果所需的代码量大幅减少!
为什么我们推荐使用 Redux Toolkit
作为 Redux 维护者,我们的立场是:
我们建议所有 Redux 用户都使用 Redux Toolkit 编写代码,因为它能简化代码并消除常见的 Redux 错误和缺陷!
早期 Redux 模式的"模板代码"和复杂性从来都不是 Redux 的必要组成部分。这些模式存在仅因为:
-
原始的"Flux 架构"采用了类似方法
-
早期 Redux 文档展示过使用 action 类型常量实现按类型分文件
-
JavaScript 默认是可变语言,编写不可变更需要手动对象扩展和数组更新
-
Redux 最初仅用几周时间开发,特意设计为少量基础 API
此外,Redux 社区采用的一些特定方法增加了额外模板:
-
强调使用
redux-saga中间件作为编写副作用的标准方案 -
坚持手动编写 Redux action 对象的 TS 类型,创建联合类型限制可派发的 action
多年来,我们观察到开发者实际使用 Redux 的方式。见证社区编写数百个附加库处理 action 类型生成、异步逻辑、副作用和数据获取。也看到用户反复遇到的痛点:意外状态变更、几十行代码仅实现简单状态更新、代码库结构难以追踪。我们帮助过成千上万学习使用 Redux 的开发者,他们常因概念繁多和额外编码量而困惑。我们深知用户面临的问题。
Redux Toolkit 正是为解决这些问题而生!
-
简化 store 配置为单一清晰函数调用,同时保留全配置能力
-
消除意外状态变更(始终是 Redux 缺陷的首要成因)
-
无需手动编写任何 action 创建函数或 action 类型
-
消除易错的手动不可变更逻辑编写
-
支持将 Redux 功能代码集中到单个文件,无需分散到多个文件
-
提供卓越的 TS 支持,API 设计确保类型安全并最小化类型定义量
-
RTK Query 可完全替代手动编写 thunk、reducer、action 创建函数或 effect hooks 来管理数据获取和加载状态
因此:
我们特别建议用户_必须_使用 Redux Toolkit(@reduxjs/toolkit 包),而_不应_在任何新项目中继续使用传统的 redux 核心包!
即使对于现有应用,我们也建议至少将 createStore 替换为 configureStore,因为其开发模式中间件能帮助捕获现有代码中的意外数据变更和序列化错误。同时强烈建议将常用 reducer(及未来编写的所有 reducer)迁移到 createSlice —— 代码将更简洁易读,安全性的提升也将为您节省大量后续维护时间。
redux 核心包虽仍可运行,但现已被视为过时技术。其所有 API 均已通过 @reduxjs/toolkit 重新导出,且 configureStore 在保留 createStore 功能基础上,提供了更优的默认行为和配置能力。
理解底层概念确实有助于把握 Redux Toolkit 的工作机制,因此《Redux 基础教程》专门演示了无抽象层的原生 Redux 实现。但需明确,这些示例仅作为教学工具,教程最终会展示 Redux Toolkit 如何简化传统手写 Redux 代码。
若您仍单独使用 redux 核心包,现有代码将继续运行。但我们强烈建议您迁移至 @reduxjs/toolkit,并将代码升级为 Redux Toolkit API!
扩展阅读
更多详细信息请参阅以下文档及博客文章: