Skip to main content

为什么 Redux 工具包就是现在使用 Redux 的方式

什么是 Redux 工具包?

¥What is Redux Toolkit?

Redux 工具包(也简称 "RTK")是我们官方推荐的 Redux 逻辑编写方法。@reduxjs/toolkit 包围绕核心 redux 包,并包含我们认为对于构建 Redux 应用至关重要的 API 方法和常见依赖。Redux Toolkit 构建了我们建议的最佳实践,简化了大多数 Redux 任务,防止常见错误,并使编写 Redux 应用变得更加容易。

¥Redux Toolkit (also known as "RTK" for short) is our official recommended approach for writing Redux logic. The @reduxjs/toolkit package wraps around the core redux package, and contains API methods and common dependencies that we think are essential for building a Redux app. Redux Toolkit builds in our suggested best practices, simplifies most Redux tasks, prevents common mistakes, and makes it easier to write Redux applications.

如果你今天正在编写任何 Redux 逻辑,你应该使用 Redux Toolkit 来编写该代码!

¥If you are writing any Redux logic today, you should be using Redux Toolkit to write that code!

RTK 包含有助于简化许多常见用例的实用程序,包括 存储设置创建 reducer 并编写不可变的更新逻辑 甚至 立即创建整个 "切片" 状态

¥RTK includes utilities that help simplify many common use cases, including store setup, creating reducers and writing immutable update logic, and even creating entire "slices" of state at once.

无论你是设置第一个项目的全新 Redux 用户,还是想要简化现有应用的经验丰富的用户,Redux 工具包 都可以帮助你改进 Redux 代码。

¥Whether you're a brand new Redux user setting up your first project, or an experienced user who wants to simplify an existing application, Redux Toolkit can help you make your Redux code better.

提示

请参阅以下页面以了解如何将 "现代回归" 与 Redux Toolkit 结合使用:

¥See these pages to learn how to use "modern Redux" with Redux Toolkit:

Redux Toolkit 与 Redux Core 有何不同

¥How Redux Toolkit Is Different Than the Redux Core

"Redux" 是什么?

¥What Is "Redux"?

首先要问的是,"什么是 Redux?"

¥The first thing to ask is, "what is Redux?"

Redux 确实是:

¥Redux is really:

  • 包含 "global" 状态的单个存储

    ¥A single store containing "global" state

  • 当应用中发生某些情况时,将普通对象操作分派到存储

    ¥Dispatching plain object actions to the store when something happens in the app

  • 纯 reducer 函数查看这些操作并返回不可变的更新状态

    ¥Pure reducer functions looking at those actions and returning immutably updated state

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

¥While it's not required, your Redux code also normally includes:

  • 生成这些动作对象的动作创建者

    ¥Action creators that generate those action objects

  • 启用副作用的中间件

    ¥Middleware to enable side effects

  • 包含具有副作用的同步或异步逻辑的 Thunk 函数

    ¥Thunk functions that contain sync or async logic with side effects

  • 标准化状态以允许按 ID 查找项目

    ¥Normalized state to enable looking up items by ID

  • 带有 Reselect 库的记忆选择器函数,用于优化派生数据

    ¥Memoized selector functions with the Reselect library for optimizing derived data

  • Redux DevTools 扩展可查看你的操作历史记录和状态更改

    ¥The Redux DevTools Extension to view your action history and state changes

  • 动作、状态和其他函数的 TypeScript 类型

    ¥TypeScript types for actions, state, and other functions

此外,Redux 通常与 React-Redux 库一起使用,让你的 React 组件与 Redux 存储进行通信。

¥Additionally, Redux is normally used with the React-Redux library to let your React components talk to a Redux store.

Redux 核心有什么作用?

¥What Does the Redux Core Do?

Redux 核心是一个非常小的、故意不带任何意见的库。它提供了一些小的 API 原语:

¥The Redux core is a very small and deliberately unopinionated library. It provides a few small API primitives:

  • createStore 实际创建一个 Redux store

    ¥createStore to actually create a Redux store

  • combineReducers 将多个切片 reducer 组合成一个更大的 reducer

    ¥combineReducers to combine multiple slice reducers into a single larger reducer

  • applyMiddleware 将多个中间件组合成一个存储增强器

    ¥applyMiddleware to combine multiple middleware into a store enhancer

  • compose 将多个存储增强器合并为单个存储增强器

    ¥compose to combine multiple store enhancers into a single store enhancer

除此之外,应用中所有其他与 Redux 相关的逻辑都必须完全由你编写。

¥Other than that, all the other Redux-related logic in your app has to be written entirely by you.

好消息是,这意味着 Redux 可以以多种不同的方式使用。坏消息是没有辅助程序可以使你的任何代码更易于编写。

¥The good news is that this means Redux can be used in many different ways. The bad news is that there are no helpers to make any of your code easier to write.

例如,reducer 函数只是一个函数。在 Redux Toolkit 之前,你通常会使用 switch 语句和手动更新来编写该 reducer。你可能还拥有手写的动作创建器和动作类型常量:

¥For example, a reducer function is just a function. Prior to Redux Toolkit, you'd typically write that reducer with a switch statement and manual updates. You'd also probably have hand-written action creators and action type constants along with it:

Legacy hand-written Redux usage
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 bug 的第一大原因!)。将一项功能的代码分散到多个文件(例如 actions/todos.jsconstants/todos.jsreducers/todos.js)中也很常见,尽管不是严格要求。

¥None of this code specifically depends on any API from the redux core library. But, this is a lot of code to write. Immutable updates required a lot of hand-written object spreads and array operations, and it was very easy to make mistakes and accidentally mutate state in the process (always the #1 cause of Redux bugs!). It was also common, though not strictly required, to spread the code for one feature across multiple files like actions/todos.js, constants/todos.js, and reducers/todos.js.

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

¥Additionally, store setup usually required a series of steps to add commonly used middleware like thunks and enable Redux DevTools Extension support, even though these are standard tools used in almost every Redux app.

Redux 工具包有什么作用?

¥What Does Redux Toolkit Do?

虽然这些是 Redux 文档中最初显示的模式,但不幸的是,它们需要大量非常冗长且重复的代码。使用 Redux 不需要大部分样板文件。最重要的是,样板代码会导致更多犯错误的机会。

¥While these were the patterns originally shown in the Redux docs, they unfortunately require a lot of very verbose and repetitive code. Most of this boilerplate isn't necessary to use Redux. On top of that, the boilerplate-y code lead to more opportunities to make mistakes.

我们专门创建了 Redux Toolkit,以消除手写 Redux 逻辑中的 "boilerplate",防止常见错误,并提供简化标准 Redux 任务的 API。

¥We specifically created Redux Toolkit to eliminate the "boilerplate" from hand-written Redux logic, prevent common mistakes, and provide APIs that simplify standard Redux tasks.

Redux Toolkit 从两个关键 API 开始,可简化你在每个 Redux 应用中执行的最常见操作:

¥Redux Toolkit starts with two key APIs that simplify the most common things you do in every Redux app:

  • configureStore 通过单个函数调用建立了一个配置良好的 Redux 存储,包括组合 reducer、添加 thunk 中间件以及设置 Redux DevTools 集成。它也比 createStore 更容易配置,因为它采用命名选项参数。

    ¥configureStore sets up a well-configured Redux store with a single function call, including combining reducers, adding the thunk middleware, and setting up the Redux DevTools integration. It also is easier to configure than createStore, because it takes named options parameters.

  • createSlice 允许你编写使用 伊默库 的 reducer,以便能够使用 "mutating" JS 语法(如 state.value = 123)编写不可变更新,而无需扩展。它还会自动为每个 reducer 生成操作创建器函数,并根据 reducer 的名称在内部生成操作类型字符串。最后,它与 TypeScript 配合得很好。

    ¥createSlice lets you write reducers that use the Immer library to enable writing immutable updates using "mutating" JS syntax like state.value = 123, with no spreads needed. It also automatically generates action creator functions for each reducer, and generates action type strings internally based on your reducer's names. Finally, it works great with TypeScript.

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

¥That means that the code you write can be drastically simpler. For example, that same todos reducer could just be:

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 代码更短且更容易理解。每种情况下实际更新的内容也更加清晰。

¥All of the action creators and action types are generated automatically, and the reducer code is shorter and easier to understand. It's also much more clear what's actually being updated in each case.

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

¥With configureStore, the store setup can be simplified down to:

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 调用会自动补齐你手动完成的所有常规设置工作:

¥Note that this one configureStore call automatically does all the usual setup work you'd have done manually:

  • 切片 reducer 自动传递到 combineReducers()

    ¥The slice reducers were automatically passed to combineReducers()

  • redux-thunk 中间件已自动添加

    ¥The redux-thunk middleware was automatically added

  • 添加了开发模式中间件以捕获意外突变

    ¥Dev-mode middleware was added to catch accidental mutations

  • Redux DevTools 扩展已自动设置

    ¥The Redux DevTools Extension was automatically set up

  • 中间件和 DevTools 增强器组合在一起并添加到存储中

    ¥The middleware and DevTools enhancers were composed together and added to the store

同时,configureStore 提供了允许用户修改任何默认行为的选项(例如关闭 thunk 并添加 sagas,或在生产中禁用 DevTools),

¥At the same time, configureStore provides the options to let users modify any of those default behaviors (like turning off thunks and adding sagas, or disabling the DevTools in production),

从这里开始,Redux Toolkit 包含了用于常见 Redux 任务的其他 API:

¥From there, Redux Toolkit includes other APIs for common Redux tasks:

  • createAsyncThunk:抽象标准 "在异步请求之前/之后调度操作" 模式

    ¥createAsyncThunk: abstracts the standard "dispatch actions before/after an async request" pattern

  • createEntityAdapter:用于规范化状态下 CRUD 操作的预构建 reducer 和选择器

    ¥createEntityAdapter: prebuilt reducers and selectors for CRUD operations on normalized state

  • createSelector:重新导出用于记忆选择器的标准 Reselect API

    ¥createSelector: a re-export of the standard Reselect API for memoized selectors

  • createListenerMiddleware:用于运行逻辑以响应分派操作的副作用中间件

    ¥createListenerMiddleware: a side effects middleware for running logic in response to dispatched actions

最后,RTK 包还包括 "RTK 查询",这是一个针对 Redux 应用的完整数据获取和缓存解决方案,作为单独的可选 @reduxjs/toolkit/query 入口点。它允许你定义端点(REST、GraphQL 或任何异步函数),并生成完全管理数据获取、更新加载状态和缓存结果的化简器和中间件。它还会自动生成可在组件中使用的 React hooks 来获取数据,例如 const { data, isFetching } = useGetPokemonQuery('pikachu')

¥Finally, the RTK package also includes "RTK Query", a full data fetching and caching solution for Redux apps, as a separate optional @reduxjs/toolkit/query entry point. It lets you define endpoints (REST, GraphQL, or any async function), and generates a reducer and middleware that fully manage fetching data, updating loading state, and caching results. It also automatically generates React hooks that can be used in components to fetch data, like const { data, isFetching } = useGetPokemonQuery('pikachu')

这些 API 中的每一个都是完全可选的,并且是针对特定用例而设计的,你可以选择在应用中实际使用的 API。但是,强烈建议他们所有人来帮助完成这些任务。

¥Each of these APIs is completely optional and designed for specific use cases, and you can pick and choose which APIs you actually use in your app. But, all of them are highly recommended to help with those tasks.

请注意,Redux Toolkit 仍然是 "Redux"!仍然有一个存储,其中包含用于更新的分派操作对象和不可变地更新状态的化简器,以及为异步逻辑编写 thunk、管理规范化状态、使用 TypeScript 键入代码以及使用 DevTools 的能力。你只需编写更少的代码即可获得相同的结果!

¥Note that Redux Toolkit is still "Redux"! There's still a single store, with dispatched action objects for updates, and reducers that immutably update state, plus the ability to write thunks for async logic, manage normalized state, type your code with TypeScript, and use the DevTools. There's just way less code you have to write for the same results!

为什么我们希望你使用 Redux 工具包

¥Why We Want You To Use Redux Toolkit

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

¥As Redux maintainers, our opinion is:

提示

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

¥We want all Redux users to write their Redux code with Redux Toolkit, because it simplifies your code and eliminates many common Redux mistakes and bugs!

早期 Redux 模式的 "boilerplate" 和复杂性从来都不是 Redux 的必要组成部分。这些模式之所以存在,是因为:

¥The "boilerplate" and complexity of the early Redux patterns was never a necessary part of Redux. Those patterns only existed because:

  • 最初的 "通量架构" 使用了一些相同的方法

    ¥The original "Flux Architecture" used some of those same approaches

  • 早期的 Redux 文档展示了诸如操作类型常量之类的内容,以便能够按类型将代码分离到不同的文件中

    ¥The early Redux docs showed things like action type constants to enable separating code into different files by type

  • 默认情况下,JavaScript 是一种可变语言,编写不可变更新需要手动对象扩展和数组更新

    ¥JavaScript is a mutable language by default, and writing immutable updates required manual object spreads and array updates

  • Redux 最初是在短短几周内构建的,并且有意设计为仅包含一些 API 原语

    ¥Redux was originally built in just a few weeks and intentionally designed to be just a few API primitives

此外,Redux 社区还采用了一些添加额外样板的特定方法:

¥Additionally, the Redux community has adopted some specific approaches that add additional boilerplate:

  • 强调使用 redux-saga 中间件作为编写副作用的常用方法

    ¥Emphasizing use of the redux-saga middleware as a common approach for writing side effects

  • 坚持为 Redux 操作对象手写 TS 类型,并创建联合类型以限制可以在类型级别分派哪些操作

    ¥Insisting on hand-writing TS types for Redux action objects and creating union types to limit what actions can be dispatched at the type level

多年来,我们已经看到人们如何在实践中实际使用 Redux。我们已经看到社区如何编写数百个附加库来执行诸如生成操作类型和创建者、异步逻辑和副作用以及数据获取等任务。我们还看到了一直给用户带来痛苦的问题,例如意外改变状态、编写数十行代码只是为了进行一个简单的状态更新,以及难以跟踪代码库如何组合在一起。我们帮助了数千名尝试学习和使用 Redux 的用户,他们努力理解所有部分如何组合在一起,并对他们必须编写的概念数量和额外代码量感到困惑。我们知道我们的用户面临什么问题。

¥Over the years, we've seen how people actually used Redux in practice. We've seen how the community wrote hundreds of add-on libraries for tasks like generating action types and creators, async logic and side effects, and data fetching. We've also seen the problems that have consistently caused pain for our users, like accidentally mutating state, writing dozens of lines of code just to make one simple state update, and having trouble tracing how a codebase fits together. We've helped thousands of users who were trying to learn and use Redux and struggling to understand how all the pieces fit together, and were confused by the number of concepts and amount of extra code they had to write. We know what problems our users are facing.

我们专门设计了 Redux Toolkit 来解决这些问题!

¥We specifically designed Redux Toolkit to solve those problems!

  • Redux Toolkit 将存储设置简化为单个明确的函数调用,同时保留在需要时完全配置存储选项的能力

    ¥Redux Toolkit simplifies store setup down to a single clear function call, while retaining the ability to fully configure the store's options if you need to

  • Redux Toolkit 消除了意外突变,这一直是 Redux bug 的第一大原因

    ¥Redux Toolkit eliminates accidental mutations, which have always been the #1 cause of Redux bugs

  • Redux Toolkit 无需手动编写任何动作创建器或动作类型

    ¥Redux Toolkit eliminates the need to write any action creators or action types by hand

  • Redux Toolkit 消除了手动编写且容易出错的不可变更新逻辑的需要

    ¥Redux Toolkit eliminates the need to write manual and error-prone immutable update logic

  • Redux Toolkit 可以轻松地在一个文件中编写 Redux 功能的代码,而不是将其分散到多个单独的文件中

    ¥Redux Toolkit makes it easy to write a Redux feature's code in one file, instead of spreading it across multiple separate files

  • Redux Toolkit 提供出色的 TS 支持,其 API 旨在为你提供出色的类型安全性并最大限度地减少你必须在代码中定义的类型数量

    ¥Redux Toolkit offers excellent TS support, with APIs that are designed to give you excellent type safety and minimize the number of types you have to define in your code

  • RTK 查询可以消除编写任何 thunk、reducer、动作创建器或效果钩子来管理数据获取和跟踪加载状态的需要

    ¥RTK Query can eliminate the need to write any thunks, reducers, action creators, or effect hooks to manage fetching data and tracking loading state

因为这:

¥Because of this:

提示

我们特别建议我们的用户应该使用 Redux Toolkit(@reduxjs/toolkit 包),并且不要将旧版 redux 核心包用于任何新的 Redux 代码!

¥We specifically recommend that our users should use Redux Toolkit (the @reduxjs/toolkit package), and should not use the legacy redux core package for any new Redux code today!

即使对于现有应用,我们建议至少将 createStore 切换为 configureStore,因为开发模式中间件还将帮助你捕获现有代码库中的意外突变和可序列化错误。我们还想鼓励你将最常用的 reducer(以及你将来编写的任何 reducer)切换到 createSlice - 代码将更短、更容易理解,安全性的改进将节省你的时间和精力。

¥Even for existing applications, we recommend at least switching out createStore for configureStore as the dev-mode middleware will also help you catch accidental mutation and serializability errors in existing code bases. We also want to encourage you to switch the reducers you are using most (and any ones you write in the future) over to createSlice - the code will be shorter and easier to understand, and the safety improvements will save you time and effort going forward.

redux 核心包仍然有效,但今天我们认为它已经过时了。它的所有 API 也都是从 @reduxjs/toolkit 重新导出的,configureStore 执行 createStore 执行的所有操作,但具有更好的默认行为和可配置性。

¥The redux core package still works, but today we consider it to be obsolete. All of its APIs are also re-exported from @reduxjs/toolkit, and configureStore does everything createStore does but with better default behavior and configurability.

了解更底层的概念很有用,这样你就可以更好地了解 Redux Toolkit 为你做什么。这就是为什么 "Redux 基础知识" 教程展示了 Redux 的工作原理,没有抽象。但是,它仅将这些示例作为学习工具进行展示,最后向你展示 Redux Toolkit 如何简化旧的手写 Redux 代码。

¥It is useful to understand the lower-level concepts, so that you have a better understanding of what Redux Toolkit is doing for you. That's why the "Redux Fundamentals" tutorial shows how Redux works, with no abstractions. But, it shows those examples solely as a learning tool, and finishes by showing you how Redux Toolkit simplifies the older hand-written Redux code.

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

¥If you are using the redux core package by itself, your code will continue to work. But, we strongly encourage you to switch over to @reduxjs/toolkit, and update your code to use the Redux Toolkit APIs instead!

更多信息

¥Further Information

有关更多详细信息,请参阅这些文档页面和博客文章

¥See these docs pages and blog posts for more details