Redux 基础,第一部分:Redux 概述
Redux 基础,第一部分:Redux 概述
- 什么是 Redux 以及为什么您可能想要使用它
- 构成 Redux 应用程序的基本部分
介绍
欢迎来到 Redux 基础教程!本教程将向您介绍使用 Redux 的核心概念、原则和模式。在您完成本教程后,您应该了解构成 Redux 应用程序的不同部分,使用 Redux 时数据是如何流动的,以及我们推荐的构建 Redux 应用程序的标准模式。
在本教程的第一部分,我们将简要地看一下一个工作的 Redux 应用程序的最小示例,以了解其组成部分,并在 第二部分:Redux 概念和数据流 中,我们将更详细地了解这些部分以及数据在 Redux 应用程序中的流动方式。
从 第三部分:状态、动作和 Reducer 开始,我们将利用这些知识构建一个小型示例应用程序,演示这些部分如何协同工作,并讨论 Redux 在实践中的工作原理。在“手动”构建完这个工作的示例应用程序后,您可以清楚地看到发生了什么,然后我们将讨论一些通常与 Redux 一起使用的标准模式和抽象。最后,我们将看到这些低级示例如何转化为我们推荐在实际应用程序中使用的更高级别模式。
如何阅读本教程
本教程将教会您“Redux 如何工作”,以及为什么这些模式存在。
请注意,本教程有意展示了旧式的 Redux 逻辑模式,这些模式需要比我们今天作为构建 Redux 应用程序的正确方法所教授的“现代 Redux”模式(使用 Redux Toolkit)需要更多的代码,以便解释 Redux 背后的原理和概念。它不是一个生产就绪的项目。
请查看以下页面,了解如何使用“现代 Redux”和 Redux Toolkit
- 完整的“Redux Essentials”教程,它教授了“如何使用 Redux,正确的方法”,使用 Redux Toolkit 构建现实世界的应用程序。我们建议所有 Redux 学习者都应该阅读“Essentials”教程!
- Redux 基础,第八部分:使用 Redux Toolkit 的现代 Redux,它展示了如何将前面部分的低级示例转换为现代 Redux Toolkit 等效项
一旦您理解了所有部分如何协同工作,我们将看看如何使用 Redux Toolkit 来简化操作。Redux Toolkit 是使用 Redux 构建生产应用程序的推荐方法,它建立在我们将在本教程中介绍的所有概念之上。一旦您理解了这里介绍的核心概念,您将了解如何更有效地使用 Redux Toolkit。
我们尽力使这些解释对初学者友好,但我们确实需要对您已经了解的内容做出一些假设,以便我们可以专注于解释 Redux 本身。本教程假设您了解
- 熟悉 HTML & CSS.
- 熟悉 ES2015 语法和特性
- 理解 数组和对象展开运算符
- 了解 React 术语:JSX、状态、函数组件、属性 和 钩子
- 了解 异步 JavaScript 和 发出 AJAX 请求
如果您还不熟悉这些主题,我们建议您先花些时间熟悉它们,然后再回来学习 Redux。我们会在您准备好时在这里!
最后,您应该确保已在浏览器中安装了 React 和 Redux DevTools 扩展
- React DevTools 扩展
- Redux DevTools 扩展
什么是 Redux?
首先,了解这个“Redux”是什么很重要。它做什么?它能帮我解决什么问题?为什么我要使用它?
Redux 是一种用于管理和更新应用程序状态的模式和库,它使用称为“操作”的事件。它充当需要在整个应用程序中使用的状态的集中存储,并具有确保状态只能以可预测方式更新的规则。
为什么我应该使用 Redux?
Redux 帮助您管理“全局”状态 - 在应用程序的许多部分都需要使用的状态。
Redux 提供的模式和工具使您更容易理解何时、何地、为何以及如何更新应用程序中的状态,以及当这些更改发生时您的应用程序逻辑将如何运行。Redux 引导您编写可预测且可测试的代码,这有助于您确信您的应用程序将按预期工作。
我什么时候应该使用 Redux?
Redux 帮助你处理共享状态管理,但就像任何工具一样,它也有权衡。你需要学习更多概念,编写更多代码。它还会给你的代码增加一些间接性,并要求你遵循某些限制。这是短期和长期生产力之间的权衡。
Redux 在以下情况下更有用
- 你的应用程序有大量状态需要在应用程序的许多地方使用
- 应用程序状态会随着时间的推移频繁更新
- 更新该状态的逻辑可能很复杂
- 应用程序具有中等或大型代码库,并且可能由多人协作开发
并非所有应用程序都需要 Redux。花一些时间思考你正在构建的应用程序类型,并决定哪些工具最适合帮助解决你正在处理的问题。
如果你不确定 Redux 是否适合你的应用程序,以下资源可以提供更多指导
Redux 库和工具
Redux 是一个小型独立的 JS 库。但是,它通常与其他几个包一起使用
React-Redux
Redux 可以与任何 UI 框架集成,并且最常与 React 一起使用。 React-Redux 是我们的官方包,它允许你的 React 组件通过读取状态片段和分派操作来更新存储,与 Redux 存储进行交互。
Redux Toolkit
Redux Toolkit 是我们推荐的编写 Redux 逻辑的方法。它包含我们认为构建 Redux 应用程序必不可少的包和函数。Redux Toolkit 内置了我们建议的最佳实践,简化了大多数 Redux 任务,防止了常见的错误,并使编写 Redux 应用程序变得更加容易。
Redux DevTools 扩展
Redux 开发工具扩展Redux DevTools Extension 显示了 Redux 存储中状态随时间变化的历史记录。这使您可以有效地调试应用程序,包括使用“时间旅行调试”等强大技术。
Redux 基础
现在您已经了解了 Redux 的概念,让我们简要了解一下构成 Redux 应用程序的各个部分以及它的工作原理。
本页面的其余描述仅关注 Redux 核心库(redux
包)。在接下来的教程中,我们将讨论其他与 Redux 相关的包。
Redux 存储
每个 Redux 应用程序的核心是存储。 “存储”是一个容器,它保存应用程序的全局状态。
存储是一个 JavaScript 对象,它具有一些特殊的功能和能力,使其与普通的全局对象不同。
- 您绝不能直接修改或更改保存在 Redux 存储中的状态。
- 相反,唯一导致状态更新的方法是创建一个普通的动作对象,该对象描述“应用程序中发生的事情”,然后将动作分派到存储中,以告知它发生了什么。
- 当分派动作时,存储会运行根reducer函数,并让它根据旧状态和动作计算新状态。
- 最后,存储会通知订阅者状态已更新,以便 UI 可以使用新数据进行更新。
Redux 核心示例应用程序
让我们看一个 Redux 应用程序的最小工作示例 - 一个小型计数器应用程序。
由于 Redux 是一个独立的 JS 库,没有依赖项,因此此示例仅通过加载 Redux 库的单个脚本标签来编写,并使用基本的 JS 和 HTML 来创建 UI。在实践中,Redux 通常通过从 NPM 安装 Redux 包来使用,并且 UI 是使用像React这样的库创建的。
第 5 部分:UI 和 React 展示了如何将 Redux 和 React 结合使用。
让我们将这个示例分解成各个部分,看看发生了什么。
状态、动作和 Reducer
我们首先定义一个初始的 状态 值来描述应用程序
// Define an initial state value for the app
const initialState = {
value: 0
}
对于这个应用程序,我们将跟踪一个表示计数器当前值的单一数字。
Redux 应用程序通常将 JS 对象作为状态的根部分,并在该对象内包含其他值。
然后,我们定义一个 reducer 函数。reducer 接收两个参数,当前 state
和一个描述发生事件的 action
对象。当 Redux 应用程序启动时,我们还没有任何状态,因此我们提供 initialState
作为此 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”被“递增”,因此我们将动作类型写为 'counter/incremented'
。
根据动作的类型,我们需要返回一个全新的对象作为新的 state
结果,或者如果不需要更改,则返回现有的 state
对象。请注意,我们通过复制现有状态并更新副本来不可变地更新状态,而不是直接修改原始对象。
存储
现在我们有了 reducer 函数,我们可以通过调用 Redux 库的 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 函数传递给 createStore
,它使用 reducer 函数生成初始状态,并计算任何未来的更新。
UI
在任何应用程序中,用户界面都会在屏幕上显示现有状态。当用户执行某些操作时,应用程序将更新其数据,然后使用这些值重新绘制 UI。
// 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>
显示当前值。
因此,我们编写一个函数,该函数知道如何使用 store.getState()
方法从 Redux 存储中获取最新状态,然后获取该值并更新 UI 以显示它。
Redux 存储允许我们调用 store.subscribe()
并传递一个订阅者回调函数,该函数将在每次存储更新时调用。因此,我们可以将我们的 render
函数作为订阅者传递,并知道每次存储更新时,我们都可以使用最新值更新 UI。
Redux 本身是一个独立的库,可以在任何地方使用。这也意味着它可以与任何 UI 层一起使用。
调度动作
最后,我们需要通过创建描述发生事件的action对象并将其dispatch到store来响应用户输入。当我们调用store.dispatch(action)
时,store会运行reducer,计算更新后的状态,并运行订阅者来更新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)
})
在这里,我们将dispatch将使reducer在当前计数器值上加1或减1的操作。
我们还可以编写仅在特定条件为真时才dispatch操作的代码,或者编写一些异步代码,该代码在延迟后dispatch操作。
数据流
我们可以用这张图总结Redux应用程序中数据的流动。它表示如何
- 响应用户交互(如点击)dispatch操作
- store运行reducer函数来计算新的状态
- UI读取新的状态以显示新的值
(如果这些部分还不完全清楚,请不要担心!在您完成本教程的其余部分时,请记住这张图,您将看到这些部分是如何组合在一起的。)
您学到了什么
那个计数器示例很小,但它确实展示了真实Redux应用程序的所有工作部分。我们在以下部分讨论的所有内容都是基于这些基本部分的扩展。
考虑到这一点,让我们回顾一下到目前为止我们学到的内容
- Redux是一个用于管理全局应用程序状态的库
- Redux通常与React-Redux库一起使用,用于将Redux和React集成在一起
- Redux Toolkit是编写Redux逻辑的推荐方法
- Redux使用几种类型的代码
- 操作是具有
type
字段的普通对象,描述了应用程序中“发生了什么” - Reducers是根据先前状态+操作计算新状态值的函数
- Redux store在每次dispatch操作时都会运行根reducer
- 操作是具有
下一步?
现在您已经了解了 Redux 应用程序的基本组成部分,请继续学习第 2 部分:Redux 概念和数据流,我们将更详细地了解数据如何在 Redux 应用程序中流动。