Why RTK is Redux Today: details on how RTK replaces the Redux core">Why RTK is Redux Today: details on how RTK replaces the Redux core">
跳至主要内容

为什么 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 Toolkit 与 Redux 核心有何不同

什么是“Redux”?

首先要问的是,“什么是 Redux?”

Redux 实际上是

  • 一个包含“全局”状态的单一商店
  • 在应用程序中发生某些事件时,将普通对象操作分派到商店
  • 纯 reducer 函数查看这些操作并返回不可变的更新状态

虽然不是必需的,但 您的 Redux 代码通常还包括

  • 生成这些操作对象的 action creator
  • 用于启用副作用的中间件
  • 包含同步或异步逻辑以及副作用的 Thunk 函数
  • 规范化的状态,以便通过 ID 查找项目
  • 使用 Reselect 库的记忆选择器函数,用于优化派生数据
  • Redux DevTools 扩展,用于查看您的操作历史记录和状态更改
  • 用于操作、状态和其他函数的 TypeScript 类型

此外,Redux 通常与 React-Redux 库一起使用,以使您的 React 组件能够与 Redux 存储进行通信。

Redux 核心做什么?

Redux 核心是一个非常小巧且刻意不加意见的库。它提供了一些小的 API 原语

  • createStore 用于实际创建 Redux 存储
  • combineReducers 用于将多个切片 reducer 合并成一个更大的 reducer
  • applyMiddleware 用于将多个中间件合并成一个存储增强器
  • compose 用于将多个存储增强器合并成一个存储增强器

除此之外,您应用程序中的所有其他与 Redux 相关的逻辑都必须由您自己编写。

好消息是,这意味着 Redux *可以* 以多种不同的方式使用。坏消息是,没有助手可以使您的任何代码更容易编写。

例如,reducer 函数 *仅仅* 是一个函数。在 Redux Toolkit 之前,您通常会使用 switch 语句和手动更新来编写该 reducer。您可能还会与之一起手写操作创建者和操作类型常量

传统的、手写的 Redux 使用方式
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 错误的首要原因!)。将一个功能的代码分散到多个文件(如 actions/todos.jsconstants/todos.jsreducers/todos.js)中也很常见,尽管这不是严格要求的。

此外,存储设置通常需要一系列步骤来添加常用的中间件(如 thunk)并启用 Redux DevTools 扩展支持,即使这些是几乎所有 Redux 应用程序中使用的标准工具。

Redux Toolkit 做了什么?

虽然这些曾经是 Redux 文档中最初展示的模式,但不幸的是,它们需要大量非常冗长且重复的代码。大多数这些样板代码并非使用 Redux 所必需的。最重要的是,样板代码会导致更多犯错的机会。

我们专门创建了 Redux Toolkit 来消除手写 Redux 逻辑中的“样板代码”,防止常见错误,并提供简化标准 Redux 任务的 API。.

Redux Toolkit 从两个关键 API 开始,简化了每个 Redux 应用程序中最常见的操作。

  • configureStore 使用单个函数调用设置了一个配置良好的 Redux 存储,包括组合 reducer、添加 thunk 中间件以及设置 Redux DevTools 集成。它也比 createStore 更容易配置,因为它接受命名选项参数。
  • createSlice 允许您编写使用 Immer 库 的 reducer,以启用使用“变异”JS 语法(如 state.value = 123)编写不可变更新,无需使用扩展运算符。它还会自动为每个 reducer 生成动作创建器函数,并根据 reducer 的名称在内部生成动作类型字符串。最后,它与 TypeScript 非常契合。

这意味着编写的代码可以变得更加简单。例如,相同的 todos reducer 可以简化为

features/todos/todosSlice.js
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

所有动作创建器和动作类型都是自动生成的,reducer 代码更短,更容易理解。它也更清楚地表明在每种情况下实际更新了什么。

使用 configureStore,存储设置可以简化为

app/store.js
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:抽象了标准的“在异步请求之前/之后调度操作”模式
  • createEntityAdapter:为规范化状态上的 CRUD 操作预构建的 reducer 和选择器
  • createSelector:标准 Reselect API 的重新导出,用于记忆选择器
  • createListenerMiddleware:一个副作用中间件,用于在响应调度操作时运行逻辑

最后,RTK 包还包含“RTK Query”,这是一个用于 Redux 应用程序的完整数据获取和缓存解决方案,作为单独的可选 @reduxjs/toolkit/query 入口点。它允许您定义端点(REST、GraphQL 或任何异步函数),并生成一个 reducer 和中间件,完全管理获取数据、更新加载状态和缓存结果。它还自动生成可以在组件中用于获取数据的 React hook,例如 const { data, isFetching} = useGetPokemonQuery('pikachu')

这些 API 都是完全可选的,并且针对特定用例而设计,您可以选择在应用程序中实际使用哪些 API。但是,所有这些都强烈推荐用于帮助完成这些任务。

请注意,Redux Toolkit 仍然是“Redux”!仍然只有一个 store,具有用于更新的调度操作对象,以及不可变地更新状态的 reducer,以及编写 thunk 用于异步逻辑、管理规范化状态、使用 TypeScript 类型化代码以及使用 DevTools 的能力。只是您为获得相同结果而需要编写的代码少了很多!

为什么我们希望您使用 Redux Toolkit

作为 Redux 维护者,我们的观点是

提示

我们希望所有 Redux 用户都使用 Redux Toolkit 编写他们的 Redux 代码,因为它简化了您的代码并且消除了许多常见的 Redux 错误和 bug!

早期 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 Fundamentals" 教程展示了 Redux 的工作原理,没有任何抽象但是,它只将这些示例作为学习工具展示,并最终展示了 Redux Toolkit 如何简化旧的 Redux 手写代码。

如果你正在单独使用 redux 核心包,你的代码将继续工作。但是,我们强烈建议你切换到 @reduxjs/toolkit,并将你的代码更新为使用 Redux Toolkit API!

更多信息

查看这些文档页面和博客文章以了解更多详细信息