跳到主要内容

React hooks全局状态管理的实现过程(模拟Redux)


1. 创建 Redux Action

src/store/actions.ts

export enum ActionType {
LOGIN = "LOGIN",
LOGOUT = "LOGOUT",
}

src/store/constants.ts

import { Dispatch } from 'react';

interface IUserInfo {
username?: string;
userId?: string
}

export interface IState {
isLogin: boolean;
userInfo: IUserInfo;
}
export interface IAction<P = {}> {
type: string;
payload?: P
}
export interface ICreateContext {
state: IState;
dispatch: Dispatch<IAction<Partial<IState>>>
}
export const initialState: IState = {
isLogin: false,
userInfo: {},
};

2. 创建 Redux Reducer

src/store/reducer.ts

import { Reducer } from 'react';
import { ActionType } from './actions';
import { IAction, IState } from './constants';

export const appReducer: Reducer<IState, IAction> = (state, action) => {
switch (action.type) {
case ActionType.LOGIN:
return { ...state, isLogin: true, ...action.payload };
case ActionType.LOGOUT:
return { ...state, isLogin: false, userInfo: {} };
default:
return state;
}
};

3. 创建 Redux Store

src/store/store.tsx

import { createContext, useContext, useReducer } from 'react';
import { ICreateContext, initialState } from './constants';
import { appReducer } from './reducer';

const AppContext = createContext<ICreateContext>({ state: initialState, dispatch: () => { } });

interface IAppProvider {
children?: React.ReactNode
}

export const AppStore = ({ children }: IAppProvider) => {
const [state, dispatch] = useReducer(appReducer, initialState);
return (
<AppContext.Provider value={{ state, dispatch }}>
{children}
</AppContext.Provider>
);
};

export const useAppContext = () => useContext(AppContext);

4. 应用

src/App.tsx

import React from 'react';
import 'antd/dist/reset.css';
import './App.css';
import { AppStore } from './store/store';
import { LoginComp } from './component/Login';

export default class App extends React.Component {
render() {
return (
<div className="App">
<AppStore>
<LoginComp />
</AppStore>
</div>
);
}
}

src/component/Login/index.tsx

import { Button, Space, Spin, Typography } from "antd";
import { useEffect, useState } from 'react';
import { ActionType } from '../../store/actions';
import { IState } from '../../store/constants';
import { useAppContext } from '../../store/store';
import './index.less';

export const LoginComp = () => {
const { state, dispatch } = useAppContext()
const [loading, setLoading] = useState(false)
const { isLogin } = state
const payload: IState = {
isLogin: true,
userInfo: { username: "lwl测试", userId: "test" },
}

useEffect(() => {
checkLogin()
return () => {
setLoading(false)
}
}, [])

const onLogin = () => {
dispatch({
type: ActionType.LOGIN,
payload
})
localStorage.setItem("appState", JSON.stringify(payload))
checkLogin()
}

// 模拟登录验证
const checkLogin = () => {
setLoading(true)
// 这里模拟登录认证,需写入全局数据(例如用户信息、菜单等等)
new Promise((resolve, reject) => {
setTimeout(() => {
const appState = JSON.parse(localStorage.getItem("appState") || "null")
if (appState) {
dispatch({ type: ActionType.LOGIN, payload: appState })
return resolve("success")
}
return reject("error")
}, 500)
}).catch((err) => {
console.log("err:", err)
setLoading(false)
onLogout()
}).finally(() => {
setLoading(false)
})
}

const onLogout = () => {
dispatch({ type: ActionType.LOGOUT })
localStorage.removeItem("appState")
}

return <div className='app-wrapper'>
{
loading ? <Spin size={"large"} /> : <div>
{
isLogin ? <Space direction='vertical'>
<Typography.Title level={2}>欢迎您,{state.userInfo.username}</Typography.Title>
<Button type="primary" onClick={onLogout}>退出登录</Button>
</Space> : <Space direction='vertical'>
<Typography.Title level={2}>请登录</Typography.Title>
<Button type="primary" onClick={onLogin}>登录</Button>
</Space>
}
</div>
}
</div>
}

5.解释

这个示例展示了如何使用 React Hooks 结合 Context API 和 useReducer 来实现全局状态管理。让我们逐步解释这个示例的每个部分:

1. 创建 Redux Action

src/store/actions.ts 中定义了一个枚举 ActionType,用来表示不同的 action 类型。

2. 创建 Redux Reducer

src/store/reducer.ts 中定义了一个名为 appReducer 的 reducer 函数,它接收当前状态和 action,并根据 action 类型来更新状态。

3. 创建 Redux Store

src/store/store.tsx 中创建了一个 AppStore 组件,它使用了 React 的 createContextuseReducer 来创建一个全局的应用状态和 dispatch 函数。在 AppStore 中,我们将状态和 dispatch 函数提供给 AppContext.Provider,这样整个应用中的组件都可以通过 useAppContext 自定义 hook 来获取状态和 dispatch 函数。

4. 应用

src/App.tsx

src/App.tsx 中,我们使用 AppStore 包裹了整个应用,并渲染了 LoginComp 组件。

src/component/Login/index.tsx

src/component/Login/index.tsx 中,我们定义了一个名为 LoginComp 的函数组件,它是一个简单的登录组件。在这个组件中,我们通过 useAppContext 自定义 hook 获取了全局状态 statedispatch 函数。

LoginComp 组件中,我们使用了 useState 来定义了一个 loading 状态,用于控制加载状态。然后,在 useEffect 中调用了 checkLogin 函数来检查用户是否登录。在 checkLogin 函数中,我们模拟了一个登录验证的过程,如果用户已登录,就更新全局状态并保存到 localStorage 中。

在组件的 JSX 中,根据全局状态 isLogin 的值来渲染不同的内容,如果用户已登录,显示欢迎信息和退出按钮,否则显示登录按钮。

总结一下,这个示例中使用了 React Hooks 结合 Context API 和 useReducer 来实现了全局状态管理。AppStore 组件创建了一个全局的状态和 dispatch 函数,并通过 Context API 提供给整个应用。LoginComp 组件使用 useAppContext 自定义 hook 来获取全局状态和 dispatch 函数,实现了简单的登录逻辑,并根据登录状态来展示不同的内容。

这种方式的好处是,它可以将全局状态和状态更新逻辑集中管理,使得组件之间可以更方便地共享状态,并且减少了 prop drilling 的问题。同时,结合了 React Hooks 和 Context API,代码更加简洁和易读。