Skip to main content

Redux 基础知识,第 1 部分:Redux 概述

你将学到什么
  • Redux 是什么以及你可能想要使用它的原因

    ¥What Redux is and why you might want to use it

  • 构成 Redux 应用的基本部分

    ¥The basic pieces that make up a Redux app

介绍

¥Introduction

欢迎来到 Redux 基础教程!本教程将向你介绍使用 Redux 的核心概念、原则和模式。完成后,你应该了解构成 Redux 应用的不同部分、使用 Redux 时数据如何流动,以及我们构建 Redux 应用的标准推荐模式。

¥Welcome to the Redux Fundamentals tutorial! This tutorial will introduce you to the core concepts, principles, and patterns for using Redux. By the time you finish, you should understand the different pieces that make up a Redux app, how data flows when using Redux, and our standard recommended patterns for building Redux apps.

在本教程的第 1 部分中,我们将简要介绍一个正在运行的 Redux 应用的最小示例,以了解其各个部分,在 第 2 部分:Redux 概念和数据流 中,我们将更详细地了解这些部分以及数据在 Redux 应用中的流动方式。

¥In Part 1 of this tutorial, we'll briefly look at a minimal example of a working Redux app to see what the pieces are, and in Part 2: Redux Concepts and Data Flow we'll look at those pieces in more detail and how data flows in a Redux application.

第 3 部分:状态、操作和 reducer 开始,我们将利用这些知识构建一个小型示例应用,演示这些部分如何组合在一起,并讨论 Redux 在实践中如何工作。在我们完成构建工作示例应用 "用手" 以便你可以准确地看到发生了什么之后,我们将讨论 Redux 通常使用的一些标准模式和抽象。最后,我们将了解这些更底层的示例如何转换为我们建议在实际应用中实际使用的较高级别模式。

¥Starting in Part 3: State, Actions, and Reducers, we'll use that knowledge to build a small example app that demonstrates how these pieces fit together and talk about how Redux works in practice. After we finish building the working example app "by hand" so that you can see exactly what's happening, we'll talk about some of the standard patterns and abstractions typically used with Redux. Finally, we'll see how these lower-level examples translate into the higher-level patterns that we recommend for actual usage in real applications.

如何阅读本教程

¥How to Read This Tutorial

本教程将教你 "Redux 是如何工作的",以及这些模式存在的原因。

¥This tutorial will teach you "how Redux works", as well as why these patterns exist.

提醒

请注意,本教程有意展示旧式 Redux 逻辑模式,这些模式比我们使用 Redux Toolkit 教授的 "现代回归" 模式需要更多的代码,作为当今使用 Redux 构建应用的正确方法,以便解释 Redux 背后的原理和概念。它并不意味着是一个生产就绪的项目。

¥Note that this tutorial intentionally shows older-style Redux logic patterns that require more code than the "modern Redux" patterns with Redux Toolkit we teach as the right approach for building apps with Redux today, in order to explain the principles and concepts behind Redux. It's not meant to be a production-ready project.

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

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

一旦你了解了所有内容如何组合在一起,我们将考虑使用 Redux Toolkit 来简化事情。Redux Toolkit 是使用 Redux 构建生产应用的推荐方法,它建立在我们将在本教程中讨论的所有概念之上。一旦你了解了此处介绍的核心概念,你就会了解如何更有效地使用 Redux Toolkit。

¥Once you understand how everything fits together, we'll look at using Redux Toolkit to simplify things. Redux Toolkit is the recommended way to build production apps with Redux, and is built on all of the concepts that we will look at throughout this tutorial. Once you understand the core concepts covered here, you'll understand how to use Redux Toolkit more efficiently.

我们试图让这些解释对初学者友好,但我们确实需要对你已经知道的内容做出一些假设,以便我们可以专注于解释 Redux 本身。本教程假设你知道:

¥We've tried to keep these explanations beginner-friendly, but we do need to make some assumptions about what you know already so that we can focus on explaining Redux itself. This tutorial assumes that you know:

先决条件

如果你还不熟悉这些主题,我们鼓励你先花一些时间熟悉它们,然后再回来了解 Redux。当你准备好时,我们会在这里!

¥If you're not already comfortable with those topics, we encourage you to take some time to become comfortable with them first, and then come back to learn about Redux. We'll be here when you're ready!

最后,你应该确保浏览器中安装了 React 和 Redux DevTools 扩展:

¥Finally, you should make sure that you have the React and Redux DevTools extensions installed in your browser:

什么是 Redux?

¥What is Redux?

它有助于首先理解 "Redux" 的东西是什么。它有什么作用?它能帮我解决什么问题?我为什么要使用它?

¥It helps to understand what this "Redux" thing is in the first place. What does it do? What problems does it help me solve? Why would I want to use it?

Redux 是一种模式和库,用于使用名为 "actions" 的事件来管理和更新应用状态。它充当需要在整个应用中使用的状态的集中存储,并通过规则确保状态只能以可预测的方式更新。

¥Redux is a pattern and library for managing and updating application state, using events called "actions". It serves as a centralized store for state that needs to be used across your entire application, with rules ensuring that the state can only be updated in a predictable fashion.

为什么我应该使用 Redux?

¥Why Should I Use Redux?

Redux 帮助你管理 "global" 状态 - 说明应用的许多部分都需要这一点。

¥Redux helps you manage "global" state - state that is needed across many parts of your application.

Redux 提供的模式和工具使你可以更轻松地了解应用中的状态何时、何地、为何以及如何更新,以及发生这些更改时应用逻辑的行为方式。Redux 指导你编写可预测和可测试的代码,这有助于让你确信你的应用将按预期工作。

¥The patterns and tools provided by Redux make it easier to understand when, where, why, and how the state in your application is being updated, and how your application logic will behave when those changes occur. Redux guides you towards writing code that is predictable and testable, which helps give you confidence that your application will work as expected.

我什么时候应该使用 Redux?

¥When Should I Use Redux?

Redux 可帮助你处理共享状态管理,但与任何工具一样,它也有权衡。有更多的概念需要学习,还有更多的代码需要编写。它还为你的代码添加了一些间接性,并要求你遵循某些限制。这是短期和长期生产力之间的权衡。

¥Redux helps you deal with shared state management, but like any tool, it has tradeoffs. There are more concepts to learn, and more code to write. It also adds some indirection to your code, and asks you to follow certain restrictions. It's a trade-off between short term and long term productivity.

Redux 在以下情况下更有用:

¥Redux is more useful when:

  • 你拥有应用中许多地方都需要的大量应用状态

    ¥You have large amounts of application state that are needed in many places in the app

  • 应用状态随着时间的推移频繁更新

    ¥The app state is updated frequently over time

  • 更新该状态的逻辑可能很复杂

    ¥The logic to update that state may be complex

  • 该应用具有中型或大型代码库,并且可能由很多人使用

    ¥The app has a medium or large-sized codebase, and might be worked on by many people

并非所有应用都需要 Redux。花一些时间考虑你正在构建的应用类型,并确定哪些工具最适合帮助解决你正在处理的问题。

¥Not all apps need Redux. Take some time to think about the kind of app you're building, and decide what tools would be best to help solve the problems you're working on.

想知道更多?

Redux 库和工具

¥Redux Libraries and Tools

Redux 是一个小型独立 JS 库。但是,它通常与其他几个包一起使用:

¥Redux is a small standalone JS library. However, it is commonly used with several other packages:

React-Redux

Redux 可以与任何 UI 框架集成,并且最常与 React 一起使用。React-Redux 是我们的官方包,可让你的 React 组件通过读取状态片段并分派操作来更新存储来与 Redux 存储进行交互。

¥Redux can integrate with any UI framework, and is most frequently used with React. React-Redux is our official package that lets your React components interact with a Redux store by reading pieces of state and dispatching actions to update the store.

Redux 工具包

¥Redux Toolkit

Redux 工具包 是我们推荐的编写 Redux 逻辑的方法。它包含我们认为构建 Redux 应用所必需的包和函数。Redux Toolkit 构建了我们建议的最佳实践,简化了大多数 Redux 任务,防止常见错误,并使编写 Redux 应用变得更加容易。

¥Redux Toolkit is our recommended approach for writing Redux logic. It contains packages and functions 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 DevTools 扩展

¥Redux DevTools Extension

Redux DevTools 扩展 显示 Redux 存储中状态随时间变化的历史记录。这使你可以有效地调试应用,包括使用 "时间旅行调试" 等强大的技术。

¥The Redux DevTools Extension shows a history of the changes to the state in your Redux store over time. This allows you to debug your applications effectively, including using powerful techniques like "time-travel debugging".

Redux 基础知识

¥Redux Basics

现在你已经了解了 Redux 是什么,让我们简要了解一下 Redux 应用的组成部分及其工作原理。

¥Now that you know what Redux is, let's briefly look at the pieces that make up a Redux app and how it works.

信息

本页的其余描述仅关注 Redux 核心库(redux 包)。在学习本教程的其余部分时,我们将讨论其他 Redux 相关的包。

¥The rest of the description on this page focuses solely on the Redux core library (the redux package). We'll talk about the other Redux-related packages as we go through the rest of the tutorial.

Redux 存储

¥The Redux Store

每个 Redux 应用的中心都是 store。"store" 是保存应用全局状态的容器。

¥The center of every Redux application is the store. A "store" is a container that holds your application's global state.

存储是一个 JavaScript 对象,具有一些特殊的功能和能力,使其不同于普通的全局对象:

¥A store is a JavaScript object with a few special functions and abilities that make it different than a plain global object:

  • 你绝不能直接修改或更改 Redux 存储中保存的状态

    ¥You must never directly modify or change the state that is kept inside the Redux store

  • 相反,导致状态更新的唯一方法是创建一个描述 "应用中发生的事情" 的普通操作对象,然后将该操作分派到存储以告诉它发生了什么。

    ¥Instead, the only way to cause an update to the state is to create a plain action object that describes "something that happened in the application", and then dispatch the action to the store to tell it what happened.

  • 当一个动作被调度时,存储运行根 reducer 函数,并让它根据旧状态和动作计算新状态

    ¥When an action is dispatched, the store runs the root reducer function, and lets it calculate the new state based on the old state and the action

  • 最后,存储通知订阅者状态已更新,以便可以使用新数据更新 UI。

    ¥Finally, the store notifies subscribers that the state has been updated so the UI can be updated with the new data.

Redux 核心示例应用

¥Redux Core Example App

让我们看一下 Redux 应用的最小工作示例 - 一个小型计数器应用:

¥Let's look at a minimal working example of a Redux app - a small counter application:

由于 Redux 是一个独立的 JS 库,没有依赖,因此本示例仅通过为 Redux 库加载单个脚本标签来编写,并使用基本的 JS 和 HTML 作为 UI。在实践中,Redux 通常由 从 NPM 安装 Redux 包 使用,UI 是使用 React 这样的库创建的。

¥Because Redux is a standalone JS library with no dependencies, this example is written by only loading a single script tag for the Redux library, and uses basic JS and HTML for the UI. In practice, Redux is normally used by installing the Redux packages from NPM, and the UI is created using a library like React.

信息

第 5 部分:UI 和 React 展示了如何一起使用 Redux 和 React。

¥Part 5: UI and React shows how to use Redux and React together.

让我们将这个示例分解为几个单独的部分,看看发生了什么。

¥Let's break this example down into its separate parts to see what's happening.

状态、操作和 reducer

¥State, Actions, and Reducers

我们首先定义一个初始状态值来描述应用:

¥We start by defining an initial state value to describe the application:

// Define an initial state value for the app
const initialState = {
value: 0
}

对于此应用,我们将使用计数器的当前值来跟踪单个数字。

¥For this app, we're going to track a single number with the current value of our counter.

Redux 应用通常有一个 JS 对象作为状态的根部分,该对象内有其他值。

¥Redux apps normally have a JS object as the root piece of the state, with other values inside that object.

然后,我们定义一个 reducer 函数。reducer 接收两个参数,当前的 state 和描述发生了什么的 action 对象。当 Redux 应用启动时,我们还没有任何状态,因此我们提供 initialState 作为此 reducer 的默认值:

¥Then, we define a reducer function. The reducer receives two arguments, the current state and an action object describing what happened. When the Redux app starts up, we don't have any state yet, so we provide the initialState as the default value for this reducer:

// Create a "reducer" function that determines what the new state
// should be when something happens in the app
function counterReducer(state = initialState, action) {
// Reducers usually look at the type of action that happened
// to decide how to update the state
switch (action.type) {
case 'counter/incremented':
return { ...state, value: state.value + 1 }
case 'counter/decremented':
return { ...state, value: state.value - 1 }
default:
// If the reducer doesn't care about this action type,
// return the existing state unchanged
return state
}
}

操作对象始终有一个 type 字段,它是你提供的字符串,用作操作的唯一名称。type 应该是一个可读的名称,以便任何查看此代码的人都能理解它的含义。在本例中,我们使用单词 'counter' 作为我们的动作类型的前半部分,后半部分是 "发生了什么" 的描述。在本例中,我们的 'counter' 是 'incremented',所以我们将动作类型写为 'counter/incremented'

¥Action objects always have a type field, which is a string you provide that acts as a unique name for the action. The type should be a readable name so that anyone who looks at this code understands what it means. In this case, we use the word 'counter' as the first half of our action type, and the second half is a description of "what happened". In this case, our 'counter' was 'incremented', so we write the action type as 'counter/incremented'.

根据操作的类型,我们要么需要返回一个全新的对象作为新的 state 结果,要么返回现有的 state 对象(如果没有任何变化)。请注意,我们通过复制现有状态并更新副本来不可变地更新状态,而不是直接修改原始对象。

¥Based on the type of the action, we either need to return a brand-new object to be the new state result, or return the existing state object if nothing should change. Note that we update the state immutably by copying the existing state and updating the copy, instead of modifying the original object directly.

存储

¥Store

现在我们有了一个 reducer 函数,我们可以通过调用 Redux 库 createStore API 来创建一个 store 实例。

¥Now that we have a reducer function, we can create a store instance by calling the Redux library createStore API.

// Create a new Redux store with the `createStore` function,
// and use the `counterReducer` for the update logic
const store = Redux.createStore(counterReducer)

我们将 reducer 函数传递给 createStorecreateStore 使用 reducer 函数生成初始状态,并计算任何未来的更新。

¥We pass the reducer function to createStore, which uses the reducer function to generate the initial state, and to calculate any future updates.

UI

在任何应用中,用户界面都会在屏幕上显示现有状态。当用户执行某些操作时,应用将更新其数据,然后使用这些值重新绘制 UI。

¥In any application, the user interface will show existing state on screen. When a user does something, the app will update its data and then redraw the UI with those values.

// Our "user interface" is some text in a single HTML element
const valueEl = document.getElementById('value')

// Whenever the store state changes, update the UI by
// reading the latest store state and showing new data
function render() {
const state = store.getState()
valueEl.innerHTML = state.value.toString()
}

// Update the UI with the initial data
render()
// And subscribe to redraw whenever the data changes in the future
store.subscribe(render)

在这个小示例中,我们仅使用一些基本的 HTML 元素作为 UI,并使用单个 <div> 显示当前值。

¥In this small example, we're only using some basic HTML elements as our UI, with a single <div> showing the current value.

因此,我们编写一个函数,它知道如何使用 store.getState() 方法从 Redux 存储中获取最新状态,然后获取该值并更新 UI 以显示它。

¥So, we write a function that knows how to get the latest state from the Redux store using the store.getState() method, then takes that value and updates the UI to show it.

Redux 存储让我们调用 store.subscribe() 并传递一个订阅者回调函数,该函数将在每次存储更新时调用。因此,我们可以将 render 函数作为订阅者传递,并且知道每次存储更新时,我们都可以使用最新值更新 UI。

¥The Redux store lets us call store.subscribe() and pass a subscriber callback function that will be called every time the store is updated. So, we can pass our render function as the subscriber, and know that each time the store updates, we can update the UI with the latest value.

Redux 本身是一个独立的库,可以在任何地方使用。这也意味着它可以与任何 UI 层一起使用。

¥Redux itself is a standalone library that can be used anywhere. This also means that it can be used with any UI layer.

调度动作

¥Dispatching Actions

最后,我们需要通过创建描述所发生事件的操作对象并将它们分派到存储来响应用户输入。当我们调用 store.dispatch(action) 时,存储运行 reducer,计算更新的状态,并运行订阅者来更新 UI。

¥Finally, we need to respond to user input by creating action objects that describe what happened, and dispatching them to the store. When we call store.dispatch(action), the store runs the reducer, calculates the updated state, and runs the subscribers to update the UI.

// Handle user inputs by "dispatching" action objects,
// which should describe "what happened" in the app
document.getElementById('increment').addEventListener('click', function () {
store.dispatch({ type: 'counter/incremented' })
})

document.getElementById('decrement').addEventListener('click', function () {
store.dispatch({ type: 'counter/decremented' })
})

document
.getElementById('incrementIfOdd')
.addEventListener('click', function () {
// We can write logic to decide what to do based on the state
if (store.getState().value % 2 !== 0) {
store.dispatch({ type: 'counter/incremented' })
}
})

document
.getElementById('incrementAsync')
.addEventListener('click', function () {
// We can also write async logic that interacts with the store
setTimeout(function () {
store.dispatch({ type: 'counter/incremented' })
}, 1000)
})

在这里,我们将调度使 reducer 从当前计数器值加 1 或减 1 的操作。

¥Here, we'll dispatch the actions that will make the reducer add 1 or subtract 1 from the current counter value.

我们还可以编写仅在特定条件为真时调度操作的代码,或者编写一些在延迟后调度操作的异步代码。

¥We can also write code that only dispatches an action if a certain condition is true, or write some async code that dispatches an action after a delay.

数据流

¥Data Flow

我们可以使用此图来总结 Redux 应用中的数据流。它代表如何:

¥We can summarize the flow of data through a Redux app with this diagram. It represents how:

  • 调度操作以响应用户交互(例如单击)

    ¥actions are dispatched in response to a user interaction like a click

  • 存储运行 reducer 函数来计算新的状态

    ¥the store runs the reducer function to calculate a new state

  • UI 读取新状态以显示新值

    ¥the UI reads the new state to display the new values

(如果这些部分还不太清楚,请不要担心!在阅读本教程的其余部分时,请将这张图片牢记在心,你将看到这些部分是如何组合在一起的。)

¥(Don't worry if these pieces aren't quite clear yet! Keep this picture in your mind as you go through the rest of this tutorial, and you'll see how the pieces fit together.)

Redux data flow diagram

你学到了什么

¥What You've Learned

这个反例很小,但它确实展示了真实 Redux 应用的所有工作部分。我们将在以下部分中讨论的所有内容都将扩展这些基本部分。

¥That counter example was small, but it does show all the working pieces of a real Redux app. Everything we'll talk about in the following sections expands on those basic pieces.

考虑到这一点,让我们回顾一下到目前为止我们所学到的知识:

¥With that in mind, let's review what we've learned so far:

概括
  • Redux 是一个用于管理全局应用状态的库

    ¥Redux is a library for managing global application state

    • Redux 通常与 React-Redux 库一起使用,将 Redux 和 React 集成在一起

      ¥Redux is typically used with the React-Redux library for integrating Redux and React together

    • Redux Toolkit 是编写 Redux 逻辑的推荐方式

      ¥Redux Toolkit is the recommended way to write Redux logic

  • Redux 使用多种类型的代码

    ¥Redux uses several types of code

    • 操作是带有 type 字段的普通对象,并在应用中描述 "发生了什么"

      ¥Actions are plain objects with a type field, and describe "what happened" in the app

    • reducer 是根据先前状态+操作计算新状态值的函数

      ¥Reducers are functions that calculate a new state value based on previous state + an action

    • 每当调度操作时,Redux 存储都会运行根 reducer

      ¥A Redux store runs the root reducer whenever an action is dispatched

下一步是什么?

¥What's Next?

现在你已经知道 Redux 应用的基本部分是什么,请继续进行 第 2 部分:Redux 概念和数据流,我们将在其中更详细地了解数据如何流经 Redux 应用。

¥Now that you know what the basic pieces of a Redux app are, step ahead to Part 2: Redux Concepts and Data Flow, where we'll look at how data flows through a Redux app in more detail.