React 学习笔记(三): 状态管理 useContext + useReducer + useEffect

在 React 中,如果你想在组件树中共享状态(比如用户信息、登录状态等),就可以用「useContext + useReducer + useEffect」这组组合拳 🥊。

整体流程大致是这样的👇:

前端 dispatch → reducer 响应 action → 控制状态如何变化

这就像是一个简化版 Redux,但不需要额外的库。


🧩 一、context.tsx

创建上下文(Context),用来保存全局的用户状态以及更新状态的函数 dispatch

1
2
3
4
5
6
7
8
9
10
11
import { createContext, useContext, type Dispatch } from "react";
import type { UserState, UserAction } from "~/types/user";

// 🎯 定义上下文中包含的内容
export interface UserContextType {
state: UserState;
dispatch: Dispatch<UserAction>;
}

// 🌍 创建上下文变量(初始值为 undefined)
export const UserContext = createContext<UserContextType | undefined>(undefined);

🧠 小结:

  • state:存储用户信息
  • dispatch:触发状态改变的函数
  • UserContext:一个全局共享的数据源容器

🧱 二、provider.tsx

Provider 就像一个“外层包裹器”📦,负责提供上下文中的数据(state)和操作方法(dispatch)给整个应用。

1
2
3
4
import React, { useContext, useEffect, useReducer } from "react";
import { UserContext, type UserContextType } from "./context";
import { userReducer } from "./reducer";
import type { UserState } from "~/types/user";

🌱 初始化用户状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let initialState: UserState = {
authenticated: false,
user: {
id: "",
name: "",
phone: "",
province: "",
city: "",
email: "",
title: "",
organization: "",
introduction: "",
roleId: 2,
roleName: "",
roleDesc: "",
maxCpu: 2,
maxStorage: 5,
maxJob: 3,
isSuperAdmin: false,
},
};

🧩 创建自定义 Hook

1
2
3
4
5
6
export const useUserContext = () => {
const context = useContext(UserContext);
if (!context)
throw new Error("useUserContext 必须在 UserProvider 中使用!");
return context;
};

📌 这样任何组件都可以通过 useUserContext() 来获取全局用户状态。

💡 UserProvider 组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
export const UserProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [state, dispatch] = useReducer(userReducer, initialState, (init) => {
// 🧭 LAZY INITIALIZER:从 localStorage 读取用户状态
try {
const storedUser = localStorage.getItem("user");
const userId = localStorage.getItem("userId");
if (userId && storedUser) {
return {
authenticated: true,
user: JSON.parse(storedUser),
};
}
} catch (e) {
console.error(e);
}
return init; // 默认初始值
});

// ✅ 副作用:同步 localStorage(持久化用户状态)
useEffect(() => {
if (state.authenticated) {
localStorage.setItem("user", JSON.stringify(state.user));
localStorage.setItem("userId", state.user.id);
} else {
localStorage.removeItem("user");
localStorage.removeItem("userId");
}
}, [state]);

// 🌍 提供上下文(state + dispatch)
return (
<UserContext.Provider value={{ state, dispatch }}>
{children}
</UserContext.Provider>
);
};

🧠 这里做了三件事:

  • 使用 useReducer 管理状态;
  • 用 useEffect 实现本地持久化(同步到 localStorage);
  • 用 <UserContext.Provider> 将 state 和 dispatch 向全局提供。

⚙️ 三、reducer.tsx

reducer 是状态的“大脑” 🧠,
决定在不同的 action 下状态如何变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import type { UserState, UserAction } from "~/types/user";

export function userReducer(state: UserState, action: UserAction): UserState {
switch (action.type) {
case "LOGIN":
return { authenticated: true, user: action.payload };

case "LOGOUT":
return {
authenticated: false,
user: {
id: "",
name: "",
phone: "",
province: "",
city: "",
email: "",
title: "",
organization: "",
introduction: "",
roleId: 2,
roleName: "",
roleDesc: "",
maxCpu: 2,
maxStorage: 5,
maxJob: 3,
isSuperAdmin: false,
},
};

default:
return state;
}
}

📘 小贴士:

  • action.type 表示动作类型,比如 “LOGIN”、”LOGOUT”
  • action.payload 是携带的数据
  • reducer 必须是纯函数(不能直接修改 state)

🎨 四、使用示例

有了上面的 Provider,我们在页面中可以这样用👇:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function LoginButton() {
const { dispatch } = useUserContext();

return (
<button
onClick={() =>
dispatch({ type: "LOGIN", payload: { id: "1", name: "Paxton" } })
}
>
登录
</button>
);
}

function UserInfo() {
const { state } = useUserContext();

return (
<div>
{state.authenticated ? (
<p>欢迎回来,{state.user.name}</p>
) : (
<p>请先登录 🙈</p>
)}
</div>
);
}

然后在最外层包裹应用:

1
2
3
<UserProvider>
<App />
</UserProvider>

这样 App 中的所有组件都能共享登录状态 ✅。

🧭 整体关系图

1
2
3
4
5
6
7
8
9
组件 → dispatch(action)

reducer(state, action)

新的 state

Context Provider

所有 useUserContext() 的组件更新

React 学习笔记(三): 状态管理 useContext + useReducer + useEffect
http://example.com/2025/09/15/React-学习笔记-三-状态管理useContext-useReducer-useEffect/
作者
Lingkai Shi
发布于
2025年9月15日
许可协议