Redux 要点,第 3 部分:基本 Redux 数据流
如何使用
createSlice
将 reducer 逻辑的 "切片" 添加到 Redux 存储¥How to add "slices" of reducer logic to the Redux store with
createSlice
使用
useSelector
钩子读取组件中的 Redux 数据¥Reading Redux data in components with the
useSelector
hook使用
useDispatch
钩子在组件中分派操作¥Dispatching actions in components with the
useDispatch
hook
熟悉关键 Redux 术语和概念,例如 "actions"、"reducers"、"store" 和 "dispatching"。(有关这些术语的解释,请参阅 第 1 部分:Redux 概述和概念。)
¥Familiarity with key Redux terms and concepts like "actions", "reducers", "store", and "dispatching". (See Part 1: Redux Overview and Concepts for explanations of these terms.)
介绍
¥Introduction
在 第 1 部分:Redux 概述和概念 中,我们研究了 Redux 如何通过为我们提供一个放置全局应用状态的单一中心位置来帮助我们构建可维护的应用。我们还讨论了 Redux 的核心概念,例如分派操作对象、使用返回新状态值的 reducer 函数以及使用 thunk 编写异步逻辑。在 第 2 部分:Redux 工具包应用结构 中,我们看到了 Redux Toolkit 中的 configureStore
和 createSlice
以及 React-Redux 中的 Provider
和 useSelector
等 API 如何协同工作,让我们编写 Redux 逻辑并与 React 组件中的逻辑进行交互。
¥In Part 1: Redux Overview and Concepts, we looked at how Redux can help us build maintainable apps by giving us a single central place to put global app state. We also talked about core Redux concepts like dispatching action objects, using reducer functions that return new state values, and writing async logic using thunks. In Part 2: Redux Toolkit App Structure, we saw how APIs like configureStore
and createSlice
from Redux Toolkit and Provider
and useSelector
from React-Redux work together to let us write Redux logic and interact with that logic from our React components.
现在你已经了解了这些内容是什么,是时候将这些知识付诸实践了。我们将构建一个小型社交媒体源应用,其中将包含许多演示一些现实世界用例的功能。这将帮助你了解如何在自己的应用中使用 Redux。
¥Now that you have some idea of what these pieces are, it's time to put that knowledge into practice. We're going to build a small social media feed app, which will include a number of features that demonstrate some real-world use cases. This will help you understand how to use Redux in your own applications.
该示例应用并不意味着是一个完整的生产就绪项目。目标是帮助你学习 Redux API 和典型使用模式,并使用一些有限的示例为你指明正确的方向。此外,我们构建的一些早期作品稍后将进行更新,以展示更好的做事方法。请通读整个教程以了解所有使用的概念。
¥The example app is not meant as a complete production-ready project. The goal is to help you learn the Redux APIs and typical usage patterns, and point you in the right direction using some limited examples. Also, some of the early pieces we build will be updated later on to show better ways to do things. Please read through the whole tutorial to see all the concepts in use.
项目设置
¥Project Setup
在本教程中,我们创建了一个预配置的入门项目,该项目已经设置了 React 和 Redux,包括一些默认样式,并且有一个假 REST API,允许我们在应用中编写实际的 API 请求。你将使用它作为编写实际应用代码的基础。
¥For this tutorial, we've created a pre-configured starter project that already has React and Redux set up, includes some default styling, and has a fake REST API that will allow us to write actual API requests in our app. You'll use this as the basis for writing the actual application code.
首先,你可以打开并分叉此 CodeSandbox:
¥To get started, you can open and fork this CodeSandbox:
你也可以 从这个 Github 存储库克隆相同的项目。克隆存储库后,你可以使用 npm install
安装该项目的工具,并使用 npm start
启动它。
¥You can also clone the same project from this Github repo. After cloning the repo, you can install the tools for the project with npm install
, and start it with npm start
.
如果你想查看我们将要构建的最终版本,你可以查看 tutorial-steps
分行 或 看看这个 CodeSandbox 中的最终版本。
¥If you'd like to see the final version of what we're going to build, you can check out the tutorial-steps
branch, or look at the final version in this CodeSandbox.
我们要感谢 塔尼亚·拉西亚,他的 将 Redux 与 React 结合使用 教程为本页面中的示例提供了启发。它还使用了她的 原始 UI CSS 启动器 进行样式。
¥We'd like to thank Tania Rascia, whose Using Redux with React tutorial helped inspire the example in this page. It also uses her Primitive UI CSS starter for styling.
创建一个新的 Redux + React 项目
¥Creating a New Redux + React Project
完成本教程后,你可能会想要尝试处理自己的项目。我们建议使用 Vite 的 Redux 模板 作为创建新 Redux + React 项目的最快方式。它附带了已配置的 Redux Toolkit 和 React-Redux,使用 你在第 1 部分中看到的相同 "counter" 应用示例。这使你可以直接编写实际的应用代码,而无需添加 Redux 包并设置存储。
¥Once you've finished this tutorial, you'll probably want to try working on your own projects. We recommend using the Redux template for Vite as the fastest way to create a new Redux + React project. It comes with Redux Toolkit and React-Redux already configured, using the same "counter" app example you saw in Part 1. This lets you jump right into writing your actual application code without having to add the Redux packages and set up the store.
如果你想了解如何将 Redux 添加到项目的具体细节,请参阅以下说明:
¥If you want to know specific details on how to add Redux to a project, see this explanation:
Detailed Explanation: Adding Redux to a React Project
Vite 的 Redux 模板附带了 Redux Toolkit 和已配置的 React-Redux。如果你要在没有该模板的情况下从头开始设置新项目,请按照以下步骤操作:
¥The Redux template for Vite comes with Redux Toolkit and React-Redux already configured. If you're setting up a new project from scratch without that template, follow these steps:
添加
@reduxjs/toolkit
和react-redux
包¥Add the
@reduxjs/toolkit
andreact-redux
packages使用 RTK 的
configureStore
API 创建 Redux store,并传入至少一个 reducer 函数¥Create a Redux store using RTK's
configureStore
API, and pass in at least one reducer function将 Redux 存储导入到应用的入口点文件中(例如
src/index.js
)¥Import the Redux store into your application's entry point file (such as
src/index.js
)使用 React-Redux 中的
<Provider>
组件封装你的根 React 组件,例如:¥Wrap your root React component with the
<Provider>
component from React-Redux, like:
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
探索初始项目
¥Exploring the Initial Project
让我们快速浏览一下初始项目包含的内容:
¥Let's take a quick look at what the initial project contains:
/public
:HTML 主机页面模板和其他静态文件(例如图标)¥
/public
: the HTML host page template and other static files like icons/src
index.js
:应用的入口点文件。它渲染 React-Redux<Provider>
组件和主要<App>
组件。¥
index.js
: the entry point file for the application. It renders the React-Redux<Provider>
component and the main<App>
component.App.js
:主要应用组件。渲染顶部导航栏并处理其他内容的客户端路由。¥
App.js
: the main application component. Renders the top navbar and handles client-side routing for the other content.index.css
:完整应用的样式¥
index.css
: styles for the complete application/api
client.js
:一个小型 AJAX 请求客户端,允许我们发出 GET 和 POST 请求¥
client.js
: a small AJAX request client that allows us to make GET and POST requestsserver.js
:为我们的数据提供一个虚假的 REST API。我们的应用稍后将从这些虚假端点获取数据。¥
server.js
: provides a fake REST API for our data. Our app will fetch data from these fake endpoints later.
/app
Navbar.js
:渲染顶部标题和导航内容¥
Navbar.js
: renders the top header and nav contentstore.js
:创建 Redux 存储实例¥
store.js
: creates the Redux store instance
如果你现在加载应用,你应该会看到标题和欢迎消息。我们还可以打开 Redux DevTools Extension 并看到我们的初始 Redux 状态完全是空的。
¥If you load the app now, you should see the header and a welcome message. We can also open up the Redux DevTools Extension and see that our initial Redux state is entirely empty.
就这样,我们开始吧!
¥With that, let's get started!
主要帖子提要
¥Main Posts Feed
我们的社交媒体源应用的主要功能是帖子列表。随着我们的进展,我们将向此功能添加更多内容,但首先,我们的第一个目标是仅在屏幕上显示帖子条目列表。
¥The main feature for our social media feed app will be a list of posts. We'll add several more pieces to this feature as we go along, but to start off, our first goal is to only show the list of post entries on screen.
创建帖子切片
¥Creating the Posts Slice
第一步是创建一个新的 Redux "slice",其中包含我们帖子的数据。一旦我们将这些数据存储在 Redux 存储中,我们就可以创建 React 组件来在页面上显示该数据。
¥The first step is to create a new Redux "slice" that will contain the data for our posts. Once we have that data in the Redux store, we can create the React components to show that data on the page.
在 src
中,创建一个新的 features
文件夹,在 features
中放入 posts
文件夹,并添加一个名为 postsSlice.js
的新文件。
¥Inside of src
, create a new features
folder, put a posts
folder inside of features
, and add a new file named postsSlice.js
.
我们将使用 Redux Toolkit createSlice
函数来创建一个 reducer 函数,它知道如何处理我们的帖子数据。Reducer 函数需要包含一些初始数据,以便 Redux 存储在应用启动时加载这些值。
¥We're going to use the Redux Toolkit createSlice
function to make a reducer function that knows how to handle our posts data. Reducer functions need to have some initial data included so that the Redux store has those values loaded when the app starts up.
现在,我们将创建一个数组,其中包含一些假的 post 对象,以便我们可以开始添加 UI。
¥For now, we'll create an array with some fake post objects inside so that we can begin adding the UI.
我们将导入 createSlice
,定义初始 posts 数组,将其传递给 createSlice
,然后导出 createSlice
为我们生成的 posts 缩减器函数:
¥We'll import createSlice
, define our initial posts array, pass that to createSlice
, and export the posts reducer function that createSlice
generated for us:
import { createSlice } from '@reduxjs/toolkit'
const initialState = [
{ id: '1', title: 'First Post!', content: 'Hello!' },
{ id: '2', title: 'Second Post', content: 'More text' }
]
const postsSlice = createSlice({
name: 'posts',
initialState,
reducers: {}
})
export default postsSlice.reducer
每次创建新切片时,我们都需要将其 reducer 函数添加到 Redux 存储中。我们已经创建了一个 Redux 存储,但现在里面没有任何数据。打开 app/store.js
,导入 postsReducer
函数,然后更新对 configureStore
的调用,以便 postsReducer
作为名为 posts
的化简器字段传递:
¥Every time we create a new slice, we need to add its reducer function to our Redux store. We already have a Redux store being created, but right now it doesn't have any data inside. Open up app/store.js
, import the postsReducer
function, and update the call to configureStore
so that the postsReducer
is being passed as a reducer field named posts
:
import { configureStore } from '@reduxjs/toolkit'
import postsReducer from '../features/posts/postsSlice'
export default configureStore({
reducer: {
posts: postsReducer
}
})
这告诉 Redux,我们希望顶层状态对象内部有一个名为 posts
的字段,并且当调度操作时,state.posts
的所有数据将由 postsReducer
函数更新。
¥This tells Redux that we want our top-level state object to have a field named posts
inside, and all the data for state.posts
will be updated by the postsReducer
function when actions are dispatched.
我们可以通过打开 Redux DevTools Extension 并查看当前状态内容来确认这是否有效:
¥We can confirm that this works by opening the Redux DevTools Extension and looking at the current state contents:
显示帖子列表
¥Showing the Posts List
现在我们的存储中有一些帖子数据,我们可以创建一个显示帖子列表的 React 组件。与我们的提要帖子功能相关的所有代码都应位于 posts
文件夹中,因此请继续在其中创建一个名为 PostsList.js
的新文件。
¥Now that we have some posts data in our store, we can create a React component that shows the list of posts. All of the code related to our feed posts feature should go in the posts
folder, so go ahead and create a new file named PostsList.js
in there.
如果我们要渲染帖子列表,我们需要从某个地方获取数据。React 组件可以使用 React-Redux 库中的 useSelector
钩子从 Redux 存储中读取数据。你编写的 "选择器功能" 将以整个 Redux state
对象作为参数来调用,并且应该从存储中返回该组件需要的特定数据。
¥If we're going to render a list of posts, we need to get the data from somewhere. React components can read data from the Redux store using the useSelector
hook from the React-Redux library. The "selector functions" that you write will be called with the entire Redux state
object as a parameter, and should return the specific data that this component needs from the store.
我们最初的 PostsList
组件将从 Redux 存储中读取 state.posts
值,然后循环遍历帖子数组并将每个帖子显示在屏幕上:
¥Our initial PostsList
component will read the state.posts
value from the Redux store, then loop over the array of posts and show each of them on screen:
import React from 'react'
import { useSelector } from 'react-redux'
export const PostsList = () => {
const posts = useSelector(state => state.posts)
const renderedPosts = posts.map(post => (
<article className="post-excerpt" key={post.id}>
<h3>{post.title}</h3>
<p className="post-content">{post.content.substring(0, 100)}</p>
</article>
))
return (
<section className="posts-list">
<h2>Posts</h2>
{renderedPosts}
</section>
)
}
然后,我们需要更新 App.js
中的路由,以便显示 PostsList
组件而不是 "welcome" 消息。将 PostsList
组件导入到 App.js
中,并将欢迎文本替换为 <PostsList />
。我们还将把它封装在 React 片段 中,因为我们很快就会向主页添加其他内容:
¥We then need to update the routing in App.js
so that we show the PostsList
component instead of the "welcome" message. Import the PostsList
component into App.js
, and replace the welcome text with <PostsList />
. We'll also wrap it in a React Fragment, because we're going to add something else to the main page soon:
import React from 'react'
import {
BrowserRouter as Router,
Switch,
Route,
Redirect
} from 'react-router-dom'
import { Navbar } from './app/Navbar'
import { PostsList } from './features/posts/PostsList'
function App() {
return (
<Router>
<Navbar />
<div className="App">
<Switch>
<Route
exact
path="/"
render={() => (
<React.Fragment>
<PostsList />
</React.Fragment>
)}
/>
<Redirect to="/" />
</Switch>
</div>
</Router>
)
}
export default App
添加后,我们应用的主页现在应如下所示:
¥Once that's added, the main page of our app should now look like this:
进步!我们已向 Redux 存储添加了一些数据,并将其显示在 React 组件的屏幕上。
¥Progress! We've added some data to the Redux store, and shown it on screen in a React component.
添加新帖子
¥Adding New Posts
很高兴看到人们写的帖子,但我们希望能够写我们自己的帖子。让我们创建一个 "添加新帖子" 表单,让我们可以编写帖子并保存它们。
¥It's nice to look at posts people have written, but we'd like to be able to write our own posts. Let's create an "Add New Post" form that lets us write posts and save them.
我们将首先创建空表单并将其添加到页面。然后,我们将表单连接到 Redux 存储,以便在单击 "保存帖子" 按钮时添加新帖子。
¥We'll create the empty form first and add it to the page. Then, we'll connect the form to our Redux store so that new posts are added when we click the "Save Post" button.
添加新的帖子表单
¥Adding the New Post Form
在 posts
文件夹中创建 AddPostForm.js
。我们将为帖子标题添加文本输入,并为帖子正文添加文本区域:
¥Create AddPostForm.js
in our posts
folder. We'll add a text input for the post title, and a text area for the body of the post:
import React, { useState } from 'react'
export const AddPostForm = () => {
const [title, setTitle] = useState('')
const [content, setContent] = useState('')
const onTitleChanged = e => setTitle(e.target.value)
const onContentChanged = e => setContent(e.target.value)
return (
<section>
<h2>Add a New Post</h2>
<form>
<label htmlFor="postTitle">Post Title:</label>
<input
type="text"
id="postTitle"
name="postTitle"
value={title}
onChange={onTitleChanged}
/>
<label htmlFor="postContent">Content:</label>
<textarea
id="postContent"
name="postContent"
value={content}
onChange={onContentChanged}
/>
<button type="button">Save Post</button>
</form>
</section>
)
}
将该组件导入到 App.js
中,并将其添加到 <PostsList />
组件的正上方:
¥Import that component into App.js
, and add it right above the <PostsList />
component:
<Route
exact
path="/"
render={() => (
<React.Fragment>
<AddPostForm />
<PostsList />
</React.Fragment>
)}
/>
你应该会看到该表单显示在页眉正下方的页面中。
¥You should see the form show up in the page right below the header.
保存帖子条目
¥Saving Post Entries
现在,让我们更新 posts 切片以将新的帖子条目添加到 Redux 存储中。
¥Now, let's update our posts slice to add new post entries to the Redux store.
我们的帖子切片负责处理帖子数据的所有更新。在 createSlice
调用内部,有一个名为 reducers
的对象。现在,它是空的。我们需要在其中添加一个 reducer 函数来处理添加帖子的情况。
¥Our posts slice is responsible for handling all updates to the posts data. Inside of the createSlice
call, there's an object called reducers
. Right now, it's empty. We need to add a reducer function inside of there to handle the case of a post being added.
在 reducers
内部,添加一个名为 postAdded
的函数,它将接收两个参数:当前 state
值和已分派的 action
对象。由于 posts 切片只知道它负责的数据,因此 state
参数将是帖子数组本身,而不是整个 Redux 状态对象。
¥Inside of reducers
, add a function named postAdded
, which will receive two arguments: the current state
value, and the action
object that was dispatched. Since the posts slice only knows about the data it's responsible for, the state
argument will be the array of posts by itself, and not the entire Redux state object.
action
对象将把我们的新帖子条目作为 action.payload
字段,我们将把这个新帖子对象放入 state
数组中。
¥The action
object will have our new post entry as the action.payload
field, and we'll put that new post object into the state
array.
当我们编写 postAdded
的 reducer 函数时,createSlice
会自动生成一个同名的 "动作创造者" 函数。我们可以导出该操作创建者并在 UI 组件中使用它,以便在用户单击 "保存帖子" 时调度该操作。
¥When we write the postAdded
reducer function, createSlice
will automatically generate an "action creator" function with the same name. We can export that action creator and use it in our UI components to dispatch the action when the user clicks "Save Post".
const postsSlice = createSlice({
name: 'posts',
initialState,
reducers: {
postAdded(state, action) {
state.push(action.payload)
}
}
})
export const { postAdded } = postsSlice.actions
export default postsSlice.reducer
记住:reducer 函数必须始终通过复制来创建不可变的新状态值!在 createSlice()
内调用 Array.push()
之类的修改函数或修改 state.someField = someValue
之类的对象字段是安全的,因为它使用 Immer 库在内部将这些突变转换为安全的不可变更新,但不要尝试改变 createSlice
之外的任何数据!
¥Remember: reducer functions must always create new state values immutably, by making copies! It's safe to call mutating functions like Array.push()
or modify object fields like state.someField = someValue
inside of createSlice()
, because it converts those mutations into safe immutable updates internally using the Immer library, but don't try to mutate any data outside of createSlice
!
调度 "帖子已添加" 行动
¥Dispatching the "Post Added" Action
我们的 AddPostForm
有文本输入和 "保存帖子" 按钮,但该按钮还没有执行任何操作。我们需要添加一个点击处理程序,该处理程序将分派 postAdded
操作创建者并传入一个包含用户编写的标题和内容的新帖子对象。
¥Our AddPostForm
has text inputs and a "Save Post" button, but the button doesn't do anything yet. We need to add a click handler that will dispatch the postAdded
action creator and pass in a new post object containing the title and content the user wrote.
我们的帖子对象还需要有一个 id
字段。目前,我们最初的测试帖子使用了一些假号码作为他们的 ID。我们可以编写一些代码来计算出下一个递增的 ID 号应该是什么,但如果我们生成一个随机的唯一 ID 会更好。Redux Toolkit 有一个 nanoid
函数,我们可以使用它。
¥Our post objects also need to have an id
field. Right now, our initial test posts are using some fake numbers for their IDs. We could write some code that would figure out what the next incrementing ID number should be, but it would be better if we generated a random unique ID instead. Redux Toolkit has a nanoid
function we can use for that.
我们将在 第 4 部分:使用 Redux 数据 中详细讨论生成 ID 和分派操作。
¥We'll talk more about generating IDs and dispatching actions in Part 4: Using Redux Data.
为了从组件分派操作,我们需要访问存储的 dispatch
函数。我们通过从 React-Redux 调用 useDispatch
钩子来获得这一点。我们还需要将 postAdded
动作创建器导入到该文件中。
¥In order to dispatch actions from a component, we need access to the store's dispatch
function. We get this by calling the useDispatch
hook from React-Redux. We also need to import the postAdded
action creator into this file.
一旦我们的组件中提供了 dispatch
函数,我们就可以在点击处理程序中调用 dispatch(postAdded())
。我们可以从 React 组件 useState
钩子中获取标题和内容值,生成一个新的 ID,然后将它们组合到一个新的 post 对象中,然后传递给 postAdded()
。
¥Once we have the dispatch
function available in our component, we can call dispatch(postAdded())
in a click handler. We can take the title and content values from our React component useState
hooks, generate a new ID, and put them together into a new post object that we pass to postAdded()
.
import React, { useState } from 'react'
import { useDispatch } from 'react-redux'
import { nanoid } from '@reduxjs/toolkit'
import { postAdded } from './postsSlice'
export const AddPostForm = () => {
const [title, setTitle] = useState('')
const [content, setContent] = useState('')
const dispatch = useDispatch()
const onTitleChanged = e => setTitle(e.target.value)
const onContentChanged = e => setContent(e.target.value)
const onSavePostClicked = () => {
if (title && content) {
dispatch(
postAdded({
id: nanoid(),
title,
content
})
)
setTitle('')
setContent('')
}
}
return (
<section>
<h2>Add a New Post</h2>
<form>
{/* omit form inputs */}
<button type="button" onClick={onSavePostClicked}>
Save Post
</button>
</form>
</section>
)
}
现在,尝试输入标题和一些文本,然后单击 "保存帖子"。你应该会在帖子列表中看到该帖子的新项目。
¥Now, try typing in a title and some text, and click "Save Post". You should see a new item for that post show up in the posts list.
恭喜!你刚刚构建了第一个可用的 React + Redux 应用!
¥Congratulations! You've just built your first working React + Redux app!
这显示了完整的 Redux 数据流周期:
¥This shows the complete Redux data flow cycle:
我们的帖子列表使用
useSelector
从存储读取初始帖子集并渲染初始 UI¥Our posts list read the initial set of posts from the store with
useSelector
and rendered the initial UI我们发送了包含新帖子条目数据的
postAdded
操作¥We dispatched the
postAdded
action containing the data for the new post entryposts 缩减程序看到了
postAdded
操作,并使用新条目更新了 posts 数组¥The posts reducer saw the
postAdded
action, and updated the posts array with the new entryRedux 存储告诉 UI 某些数据已更改
¥The Redux store told the UI that some data had changed
帖子列表读取更新的帖子数组,并重新渲染自身以显示新帖子
¥The posts list read the updated posts array, and re-rendered itself to show the new post
我们在此之后添加的所有新功能都将遵循你在此处看到的相同基本模式:添加状态切片、编写 reducer 函数、分派操作以及根据 Redux 存储中的数据渲染 UI。
¥All the new features we'll add after this will follow the same basic patterns you've seen here: adding slices of state, writing reducer functions, dispatching actions, and rendering the UI based on data from the Redux store.
我们可以检查 Redux DevTools 扩展来查看我们调度的操作,并查看 Redux 状态如何响应该操作而更新。如果我们单击操作列表中的 "posts/postAdded"
条目,"行动" 选项卡应如下所示:
¥We can check the Redux DevTools Extension to see the action we dispatched, and look at how the Redux state was updated in response to that action. If we click the "posts/postAdded"
entry in the actions list, the "Action" tab should look like this:
"差异" 选项卡还应该向我们显示 state.posts
添加了一项新项目,该项目位于索引 2 处。
¥The "Diff" tab should also show us that state.posts
had one new item added, which is at index 2.
请注意,我们的 AddPostForm
组件内部有一些 React useState
钩子,用于跟踪用户输入的标题和内容值。请记住,Redux 存储应该只包含应用被视为 "global" 的数据!在这种情况下,只有 AddPostForm
需要了解输入字段的最新值,因此我们希望将该数据保留在 React 组件状态中,而不是尝试将临时数据保留在 Redux 存储中。当用户完成表单后,我们调度一个 Redux 操作,根据用户输入使用最终值更新存储。
¥Notice that our AddPostForm
component has some React useState
hooks inside, to keep track of the title and content values the user is typing in. Remember, the Redux store should only contain data that's considered "global" for the application! In this case, only the AddPostForm
will need to know about the latest values for the input fields, so we want to keep that data in React component state instead of trying to keep the temporary data in the Redux store. When the user is done with the form, we dispatch a Redux action to update the store with the final values based on the user input.
你学到了什么
¥What You've Learned
让我们回顾一下你在本节中学到的内容:
¥Let's recap what you've learned in this section:
Redux 状态由 "reducer 函数" 更新:
¥Redux state is updated by "reducer functions":
reducer 总是通过复制现有状态值并使用新数据修改副本来不可变地计算新状态
¥Reducers always calculate a new state immutably, by copying existing state values and modifying the copies with the new data
Redux Toolkit
createSlice
函数为你生成 "切片 reducer" 函数,并让你编写 "mutating" 代码,这些代码将转换为安全的不可变更新¥The Redux Toolkit
createSlice
function generates "slice reducer" functions for you, and lets you write "mutating" code that is turned into safe immutable updates这些切片缩减器函数被添加到
configureStore
中的reducer
字段,并定义 Redux 存储内的数据和状态字段名称¥Those slice reducer functions are added to the
reducer
field inconfigureStore
, and that defines the data and state field names inside the Redux store
React 组件使用
useSelector
钩子从存储中读取数据¥React components read data from the store with the
useSelector
hook选择器函数接收整个
state
对象,并且应该返回一个值¥Selector functions receive the whole
state
object, and should return a value每当 Redux 存储更新时,选择器都会重新运行,如果它们返回的数据发生更改,组件将重新渲染
¥Selectors will re-run whenever the Redux store is updated, and if the data they return has changed, the component will re-render
React 组件使用
useDispatch
钩子调度动作来更新存储¥React components dispatch actions to update the store using the
useDispatch
hookcreateSlice
将为我们添加到切片的每个 reducer 生成动作创建器函数¥
createSlice
will generate action creator functions for each reducer we add to a slice在组件中调用
dispatch(someActionCreator())
来调度操作¥Call
dispatch(someActionCreator())
in a component to dispatch an actionreducer 将运行,检查此操作是否相关,并在适当的情况下返回新状态
¥Reducers will run, check to see if this action is relevant, and return new state if appropriate
诸如表单输入值之类的临时数据应保留为 React 组件状态。当用户完成表单操作后,调度 Redux 操作来更新存储。
¥Temporary data like form input values should be kept as React component state. Dispatch a Redux action to update the store when the user is done with the form.
到目前为止,该应用的外观如下:
¥Here's what the app looks like so far:
下一步是什么?
¥What's Next?
现在你已经了解了基本的 Redux 数据流,请继续学习 第 4 部分:使用 Redux 数据,我们将在其中向我们的应用添加一些附加功能,并查看如何使用存储中已有数据的示例。
¥Now that you know the basic Redux data flow, move on to Part 4: Using Redux Data, where we'll add some additional functionality to our app and see examples of how to work with the data that's already in the store.