为什么 Redux Toolkit 是今天使用 Redux 的方式
什么是 Redux Toolkit?
Redux Toolkit(也称为 "RTK")是我们官方推荐的编写 Redux 逻辑的方法。@reduxjs/toolkit
包围绕核心 redux
包,并包含我们认为构建 Redux 应用程序必不可少的 API 方法和常用依赖项。Redux Toolkit 内置了我们建议的最佳实践,简化了大多数 Redux 任务,防止了常见的错误,并使编写 Redux 应用程序变得更加容易。
如果您今天正在编写任何 Redux 逻辑,您应该使用 Redux Toolkit 来编写该代码!
RTK 包含有助于简化许多常见用例的实用程序,包括商店设置、创建 reducer 和编写不可变更新逻辑,甚至一次创建整个“切片”状态。
无论您是刚开始使用 Redux 的用户,正在设置您的第一个项目,还是经验丰富的用户,想要简化现有应用程序,Redux Toolkit 都可以帮助您改进 Redux 代码。
查看以下页面,了解如何使用 Redux Toolkit 实现“现代 Redux”
- “Redux Essentials” 教程,它使用 Redux Toolkit 教授“如何以正确的方式使用 Redux”来构建实际应用,
- Redux 基础知识,第 8 部分:使用 Redux Toolkit 实现现代 Redux,它展示了如何将教程前面部分的低级示例转换为现代 Redux Toolkit 等效项
- 使用 Redux:迁移到现代 Redux,它涵盖了如何将不同类型的传统 Redux 逻辑迁移到现代 Redux 等效项
Redux Toolkit 与 Redux 核心有何不同
什么是“Redux”?
首先要问的是,“什么是 Redux?”
Redux 实际上是
- 一个包含“全局”状态的单一存储
- 当应用程序中发生某些事件时,向存储分发普通对象操作
- 纯 reducer 函数查看这些操作并返回不可变的更新状态
虽然不是必需的,但您的 Redux 代码通常还包括
- 生成这些操作对象的 action creators
- 启用副作用的中间件
- 包含同步或异步逻辑以及副作用的 thunk 函数
- 规范化状态以启用通过 ID 查找项目
- 使用 Reselect 库的记忆化选择器函数,用于优化派生数据
- Redux DevTools 扩展,用于查看您的操作历史记录和状态更改
- 用于操作、状态和其他函数的 TypeScript 类型
此外,Redux 通常与 React-Redux 库一起使用,以使您的 React 组件能够与 Redux 存储进行通信。
Redux 核心做了什么?
Redux 核心是一个非常小巧且有意保持中立的库。它提供了一些小的 API 原语
createStore
用于实际创建 Redux 存储combineReducers
用于将多个切片 reducer 合并成一个更大的 reducerapplyMiddleware
用于将多个中间件合并成一个存储增强器compose
用于将多个存储增强器合并成一个存储增强器
除此之外,您应用程序中的所有其他与 Redux 相关的逻辑都必须由您自己编写。
好消息是,这意味着 Redux 可以以多种不同的方式使用。坏消息是,没有帮助程序可以使您的任何代码更容易编写。
例如,reducer 函数仅仅是一个函数。在 Redux Toolkit 之前,您通常会使用 switch
语句和手动更新来编写该 reducer。您可能还会为它编写手动操作创建者和操作类型常量
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.id) return todo
return {
...todo,
completed: !todo.completed
}
})
default:
return state
}
}
这些代码都没有专门依赖于 redux
核心库中的任何 API。但是,这需要编写大量的代码。不可变更新需要大量手动编写的对象扩展和数组操作,并且很容易出错,并在过程中意外地修改状态(始终是 Redux 错误的 #1 原因!)。将一个功能的代码分散到多个文件(如 actions/todos.js
、constants/todos.js
和 reducers/todos.js
)也很常见,尽管这不是严格要求的。
此外,存储设置通常需要一系列步骤来添加常用的中间件(如 thunk)并启用 Redux DevTools Extension 支持,即使这些是几乎每个 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
的“可变”JS 语法编写不可变更新,无需使用扩展运算符。它还会自动为每个 reducer 生成 action creator 函数,并根据 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 creator 和 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
提供了选项,让用户可以修改任何这些默认行为**(例如关闭 thunk 并添加 saga,或在生产环境中禁用 DevTools),
从那里开始,Redux Toolkit 包含其他 API 用于常见的 Redux 任务
createAsyncThunk
:抽象出标准的“在异步请求之前/之后调度操作”模式createEntityAdapter
:针对规范化状态的 CRUD 操作的预构建 reducer 和 selectorcreateSelector
:标准 Reselect API 的重新导出,用于记忆化 selectorcreateListenerMiddleware
:一个副作用中间件,用于在响应调度操作时运行逻辑
最后,RTK 包还包含“RTK Query”,这是一个针对 Redux 应用程序的完整数据获取和缓存解决方案,作为单独的可选 @reduxjs/toolkit/query
入口点。它允许您定义端点(REST、GraphQL 或任何异步函数),并生成一个 reducer 和中间件,它们完全管理获取数据、更新加载状态和缓存结果。它还会自动生成可在组件中用于获取数据的 React 钩子,例如 const { data, isFetching } = useGetPokemonQuery('pikachu')
这些 API 中的每一个都是完全可选的,并且针对特定的用例而设计,并且 **您可以选择您在应用程序中实际使用的 API**。但是,所有这些都强烈建议用于帮助完成这些任务。
请注意,**Redux Toolkit 仍然是“Redux”!** 仍然只有一个存储,带有用于更新的调度操作对象,以及用于不可变更新状态的 reducer,以及编写 thunk 用于异步逻辑、管理规范化状态、使用 TypeScript 对代码进行类型化以及使用 DevTools 的能力。**您只需要为相同的结果编写更少的代码!**
为什么我们希望您使用 Redux Toolkit
作为 Redux 维护者,我们的观点是
我们希望所有 Redux 用户都使用 Redux Toolkit 编写他们的 Redux 代码,因为它简化了您的代码并且消除了许多常见的 Redux 错误和漏洞!
早期 Redux 模式中的“样板代码”和复杂性从来都不是 Redux 的必要部分。这些模式只存在是因为
- 最初的“Flux 架构”使用了一些相同的做法
- 早期的 Redux 文档展示了诸如操作类型常量之类的东西,以允许根据类型将代码分离到不同的文件中
- JavaScript 默认情况下是一种可变语言,编写不可变更新需要手动对象扩展和数组更新
- Redux 最初是在短短几周内构建的,并且有意设计为仅包含几个 API 原语
此外,Redux 社区已经采用了一些特定的方法,这些方法会添加额外的样板代码
- 强调使用
redux-saga
中间件作为编写副作用的常用方法 - 坚持手动为 Redux 操作对象编写 TS 类型并创建联合类型以限制在类型级别可以调度哪些操作
多年来,我们见证了人们在实践中如何使用 Redux。我们看到了社区如何为生成动作类型和创建者、异步逻辑和副作用以及数据获取等任务编写了数百个附加库。我们也看到了始终困扰用户的问题,例如意外地修改状态、仅仅为了进行一次简单的状态更新而编写数十行代码,以及难以追踪代码库的结构。我们帮助了数千名试图学习和使用 Redux 的用户,他们难以理解所有部分如何组合在一起,并对他们必须编写的概念数量和额外代码量感到困惑。我们 *知道* 我们的用户面临着哪些问题。
我们专门设计了 Redux Toolkit 来解决这些问题!
- Redux Toolkit 将商店设置简化为一个清晰的函数调用,同时保留了在需要时完全配置商店选项的能力。
- Redux Toolkit 消除了意外的突变,这始终是 Redux 错误的首要原因。
- Redux Toolkit 消除了手动编写任何动作创建者或动作类型的必要性。
- Redux Toolkit 消除了编写手动且容易出错的不可变更新逻辑的必要性。
- Redux Toolkit 使得在一个文件中轻松编写 Redux 功能的代码,而不是将其分散到多个单独的文件中。
- Redux Toolkit 提供了出色的 TS 支持,其 API 旨在为您提供出色的类型安全性,并最大限度地减少您在代码中必须定义的类型数量。
- RTK Query 可以消除编写 *任何* thunk、reducer、动作创建者或效果钩子来管理获取数据和跟踪加载状态的必要性。
因此
我们特别建议我们的用户 *应该* 使用 Redux Toolkit(@reduxjs/toolkit
包),并且 *不应该* 在今天为任何新的 Redux 代码使用传统的 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!
更多信息
查看这些文档页面和博客文章以了解更多详细信息