Redux 和 Redux Toolkit 入门
什么是 Redux?
正如官方文档所述:Redux 是一种用于管理和更新全局应用程序状态的模式和库。它充当需要在整个应用程序中使用的状态的集中存储,其规则确保只能以可预测的方式更新状态(不会引起副作用或意外行为)。
因此,Redux 作为应用程序的全局状态管理器,确保状态的每个变化都经过特定且有组织的过程。
将状态视为需要在应用程序的所有组件之间共享的 JavaScript 对象。
如果您有这样的用户对象:
const user = { username: "Jane Doe", email: "email@example.com", };
如果您手动将此对象传递到应用的组件树中,则与每个组件共享此对象很快就会变得混乱。这时 Redux 就派上用场了。
Redux 的工作原理
Redux 有自己的一套术语和规则,每个组件在访问或更新数据时都应遵循这些术语和规则。最基本的是“actions”和“dispatch”。
Redux 中的操作描述了组件想要对状态执行的操作。例如:
这些任务可以转换为 Redux 可以理解和处理的“动作”类型。
定义动作
动作是简单的 JavaScript 对象,包含类型(标识动作的字符串)和可选的有效负载(用于更新状态的数据)。
以下是一些示例操作:
const emailUpdated = { type: "user/emailUpdated", payload: "email@main.co", }; const usernameUpdated = { type: "user/usernameUpdated", payload: "Hajar", }; const clearUserData = { type: "user/clearUserData", };
定义这些之后,组件就可以使用它们来指定想要对状态进行什么样的改变。
现在组件知道它们需要执行哪些“动作”以及要包含哪些数据/有效负载,因此它们需要将它们“分派”到 Redux 存储。
Dispatch 函数:向 Redux 发送 Action
`dispatch` 函数用于将 `actions` 发送到 Redux store。它是一个常规的 JavaScript 函数,唯一的区别是:
当你使用 `action` 调用 `dispatch` 时,Redux 会处理它,然后触发处理它的正确的 `reducer`。
什么是 Reducer?
`reducer` 是一个 Redux 函数,它接受当前 `state` 和 `action`,根据 `action` 类型确定如何更新 `state`,然后返回更新后的状态。
例如,如果 Reducer 收到“user/usernameUpdated”之类的操作,它会使用操作负载中的值修改状态中的用户名。一旦 Reducer 更新状态,Redux 就会通知相关组件重新渲染。
瞧!组件现在具有更新的用户数据,而不必担心 Redux 为实现这一目标所采取的步骤。
以下是 `reducer` 的示例:
const ACTION_TYPES = { UPDATE_USERNAME: "user/usernameUpdated", UPDATE_EMAIL: "user/emailUpdated", CLEAR_STATE: "user/userDataCleared", }; const initialState = { username: "", email: "", }; const userReducer = (state = initialState, action) => { switch (action.type) { case ACTION_TYPES.UPDATE_USERNAME: return { // spread the state to return a new state object ...state, username: action.payload, }; case ACTION_TYPES.UPDATE_EMAIL: return { ...state, email: action.payload, }; case ACTION_TYPES.CLEAR_STATE: return initialState; default: return state; } }; export default userReducer;
以下是组件如何“分派”“动作”:
const updateUsername = (name) => { dispatch({ type: "user/usernameUpdated", payload: name, }); }; const updateEmail = (email) => { dispatch({ type: "user/emailUpdated", payload: email, }); }; const clearUserData = () => { dispatch({ type: "user/userDataCleared", }); };
这很酷,但是你有没有注意到我们为这样一个简单的 Reducer 示例写了多少代码?
因此,您可以想象,在实际应用中引入“reducers”和“actions”需要编写多少代码。这时 Redux ToolKit 就派上用场了。
将 Redux Toolkit 与 Next.js 结合使用
要在 Next.js 项目中使用 Redux Toolkit,我们需要安装 Next.js:
npx create-next-app@latest
然后,安装 `@reduxjs/toolkit` 和 `react-redux`:
npm install @reduxjs/toolkit react-redux
**配置 Redux Store**
接下来,创建一个文件“app/redux/store.ts”来配置Redux存储。我们将使用Redux Toolkit中的“configureStore”API。
import { configureStore } from '@reduxjs/toolkit'; export default configureStore({ // The reducer will be added here later reducer: {} });
使用 Redux Provider 包装应用程序
为了使您的 Redux 存储在整个应用程序中可访问,请使用“react-redux”中的“Provider”组件包装您的根组件,并将“store”传递给它。
"use client"; import { Provider } from "react-redux"; import store from "@/app/redux/store"; import MainContent from "@/app/components/MainContent"; export default function Home() { return (); }
使用 Redux Toolkit 设置切片
在 Redux Toolkit 中,切片管理“状态”和“reducers”。要创建切片,请使用“@reduxjs/toolkit”中的“createSlice”函数。
import { createSlice } from "@reduxjs/toolkit"; const initialState = { username: "", email: "", }; export const userSlice = createSlice({ name: "user", initialState, reducers: { usernameUpdated: (state, action) => { state.username = action.payload; }, emailUpdated: (state, action) => { state.email = action.payload; }, userDataCleared: (state) => { state.username = ""; state.email = ""; }, }, }); export default userSlice.reducer;
`createSlice` 接受的主要参数是:
**注意**:你有没有注意到我们在这里是如何直接修改状态的?在 `createSlice` 中,我们可以使用该语法来简化更新状态的逻辑 - 特别是对于深度嵌套的值。然而,在底层,事情是这样的:
Redux Toolkit 允许我们在 Reducer 中编写“改变”逻辑。它实际上不会改变状态,因为它使用了 Immer 库,该库会检测“草稿状态”的更改并根据这些更改生成全新的不可变状态。
但是等等,我们如何检查“action”类型并将它们映射到“reducers”,就像我们在前面的代码中所做的那样?这里有个很酷的部分:你不需要!一旦你定义了“reducers”,Redux Toolkit 会自动为每个动作生成动作创建器,我们可以轻松地从切片文件中导出它们,如下所示:
// app/redux/userSlice.ts import { createSlice } from "@reduxjs/toolkit"; const initialState = { username: "", email: "", }; export const userSlice = createSlice({ name: "user", initialState, reducers: { usernameUpdated: (state, action) => { state.username = action.payload; }, emailUpdated: (state, action) => { state.email = action.payload; }, userDataCleared: (state) => { state = initialState; }, }, }); // the actions are exported to be used by any component now export const { usernameUpdated, emailUpdated, userDataCleared } = userSlice.actions; export default userSlice.reducer;
然后我们可以使用 `userSlice.reducer` 来更新 `store`:
import { configureStore } from "@reduxjs/toolkit"; import userReducer from "./userSlice"; export default configureStore({ reducer: { user: userReducer, // we can inroduce more reducers from different slices here // posts: postsReducer }, });
使用选择器和调度动作
现在已经设置了存储和切片,我们可以使用“选择器”来获取数据并分派操作来更新它。
`selectors` 用于访问状态数据并使其可供应用程序的组件使用。以下是在 `userSlice` 文件中创建和导出它们的方法:
export const selectUsername = (state) => state.user.username; export const selectEmail = (state) => state.user.email;
现在我们可以使用选择器数据和分派操作:
import { useSelector, useDispatch } from "react-redux"; import { selectUsername, selectEmail } from "@/redux/userSlice"; import { usernameUpdated, emailUpdated, userDataCleared } from "@/redux/userSlice"; const UserComponent = () => { const username = useSelector(selectUsername); const email = useSelector(selectEmail); const dispatch = useDispatch(); const handleUpdateUsername = (name: string) => dispatch(usernameUpdated(name)); const handleUpdateEmail = (email: string) => dispatch(emailUpdated(email)); const handleClearUserData = () => dispatch(userDataCleared()); return (); };Username: {username}
Email: {email}