React+ts+vite脚手架搭建(三)【状态管理篇】

前言

上一篇我们介绍了脚手架搭建的接口篇,如不清楚的话,可以移步:React+ts+vite脚手架搭建(二)【接口篇】

状态管理也是每个脚手架都必备的,用于统一管理应用数据,解决状态共享等难题。

这里我用的是rematch,也可以替换成别的状态管理库,比如zustand

具体步骤

下载依赖

js 复制代码
yarn add react-redux @rematch/core redux-logger @types/redux-logger
  • react-redux:提供<Provider> 组件,让 Reduxstore 能被整个 React 组件树访问
  • @rematch/core:基于Redux的轻量级封装,简化了Redux的使用逻辑
  • redux-logger:是Redux的中间件,用于在控制台打印状态变化的日志,方便调试
  • @types/redux-logger: redux-loggerTypeScript 类型定义文件

新建文件

在之前创建的项目目录结构中的状态区新建store.tsmodels.ts文件:

初始化store

这里我们将初始化store并导出,最后将store实例放在最顶层组件,用于存储全局的状态和数据:

  • init初始化store函数
  • 添加配置models
    • 传入从models引入的models,这个模型定义了应用的所有状态和修改逻辑
  • 配置Redux中间件
    • 添加createLogger,用于在控制台输出状态变化信息便于调试
  • 定义Store类型,并导出
    • 用于在需要传入store实例的地方提供类型约束
  • 定义Dispatch类型,并导出
    • 可以在组件中使用dispatch时,获得类型提示和校验
  • 定义RootState类型,并导出
    • 用于在组件中获取状态时提供类型提示(如useSelector
js 复制代码
// init: 用于初始化 Rematch store 的函数
// RematchDispatch: 类型,用于定义符合 Rematch 规范的 dispatch 类型
// RematchRootState: 类型,用于生成根状态的类型
import { init, RematchDispatch, RematchRootState } from "@rematch/core";

// 从 redux-logger 导入日志工具,用于在控制台打印 Redux 状态变化日志
import { createLogger } from "redux-logger";

// 导入应用的模型定义:
// models: 实际的状态模型集合(包含各个模块的 state、reducers、effects 等)
// RootModel: 根模型的类型定义,用于类型推导
import { models, RootModel } from "./models";

// 初始化 Rematch store
export const store = init({
  // 传入应用的状态模型,这些模型定义了应用的所有状态和修改逻辑
  models,
  // Redux 相关配置
  redux: {
    // 配置 Redux 中间件,这里添加了日志中间件
    // createLogger() 会生成一个日志中间件,用于在控制台输出状态变化信息(便于调试)
    middlewares: [createLogger()],
  },
});

// 定义 Store 类型,其类型与初始化后的 store 保持一致
// 用于在需要传入 store 实例的地方提供类型约束
export type Store = typeof store;

// 定义 Dispatch 类型,基于 RootModel 生成符合 Rematch 规范的 dispatch 类型
// 在组件中使用 dispatch 时会获得类型提示和校验
export type Dispatch = RematchDispatch<RootModel>;

// 定义 RootState 类型,基于 RootModel 生成根状态的类型
// 用于在组件中获取状态时提供类型提示(如使用 useSelector 时)
export type RootState = RematchRootState<RootModel>;

初始化models

我们在上面初始化store.ts文件时,引用了models.ts文件里的两个变量:modelsRootModel,我们现在在models.ts文件中定义这两个变量:

js 复制代码
import { Models } from '@rematch/core';

export interface RootModel extends Models<RootModel> {
  
}

export const models: RootModel = {
    
}

RootModel接口定义了整个应用的状态模型类型规范;

models对象是实际存放各个业务模块状态类型的容器;

我们先将这两个地方的内容空出来,后面再进行补充。

将Provider作为应用的顶层组件

使用状态管理的最后一步,就是将状态实例给放到全局,我们在main.tsx文件中修改:

  • 引入Provider组件
  • 引入store实例
  • Provider组件包裹根组件并传入store实例
js 复制代码
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { HashRouter } from "react-router-dom";
import './style/index.css'
import App from './App'

import { Provider } from "react-redux";
import { store } from "./model/store";


createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <Provider store={store}>
      <HashRouter> 
        <App />
      </HashRouter>
    </Provider>
  </StrictMode>,
)

好了以上的rematch的前期使用必备的逻辑都已搭建完毕,后续我们可以正式在业务模块中使用了。

使用

现在我们实际使用一下,我们这里以用户信息为例子,假设我们从一个接口中获取用户信息,并且存储在全局,且展示出来:

mock用户信息接口

  • 我这里用Promise来模拟请求
  • 这里的类型我用了any,大家可以根据自己项目的实际情况来声明类型
js 复制代码
export function getUserInfo(): Promise<any> {
  return new Promise((resolve, reject) => {
    resolve({
      name: "张三",
      age: 18,
    })
  })
}

新建homeModel.ts

  • 添加状态类型------IhomeModelState
  • 给状态赋予初始值------ useInfo: {}
  • reducers中定义更新状态的函数
  • effects中定义需要异步更改状态的逻辑
js 复制代码
import { createModel } from '@rematch/core';
import { RootModel } from '../../../model/models';



interface IhomeModelState {
}

export const home = createModel<RootModel>()({
  state: {
    useInfo: {}
  } as IhomeModelState, // initial state
  reducers: {
    setUseInfo(state, payload: any = {}) {
      return {
        ...state,
        useInfo: payload
      };
    },
  },
  effects: (dispatch) => ({

  }),
});

填充业务模块model

js 复制代码
import { Models } from "@rematch/core";
import { home } from "../page/home/model/homeModel";

export interface RootModel extends Models<RootModel> {
  home: typeof home;
}

export const models: RootModel = {
  home,
};

home页调用接口更新状态

这样我们就能在页面中看到最新的状态

js 复制代码
import { useDispatch, useSelector } from 'react-redux';
import { Dispatch } from "../../model/store";
import { RootModel } from '../../model/models';
import { useEffect } from 'react';
import { getUserInfo } from '../../service';

const Home = () => {
  const dispatch = useDispatch<Dispatch>();
  const useInfo = useSelector((state: RootModel) => state.home.useInfo);  

  useEffect(() => {
    getUserInfo().then((res) => {
      dispatch.home.setUseInfo(res);
    });
  }, []);
  
  return (
    <div>
      <h1>Home</h1>
      <p>useInfo: {JSON.stringify(useInfo?.name)}</p>
      <p>useInfo: {JSON.stringify(useInfo?.age)}</p>
    </div>
  );
};

export default Home;

补充

之前我们在初始化homeModel.ts文件的时候,有个effects的模块待补充,这里之前的解释是说要需要补充异步更改状态的逻辑 接口请求就是异步的,所以我们上述请求用户信息的接口我们也可以写在这里面:

js 复制代码
import { createModel } from '@rematch/core';
import { RootModel } from '../../../model/models';
import { getUserInfo } from '../../../service';



interface IhomeModelState {
}

export const home = createModel<RootModel>()({
  state: {
    useInfo: {}
  } as IhomeModelState, // initial state
  reducers: {
    setUseInfo(state, payload: any = {}) {
      return {
        ...state,
        useInfo: payload
      };
    },
  },
  effects: (dispatch) => ({
    async getUserInfo2() {
      const res = await getUserInfo();
      dispatch.home.setUseInfo(res);
    },
  }),
});

然后再useEffect中使用

js 复制代码
import { useDispatch, useSelector } from 'react-redux';
import { Dispatch } from "../../model/store";
import { RootModel } from '../../model/models';
import { useEffect } from 'react';
import { getUserInfo } from '../../service';

const Home = () => {
  const dispatch = useDispatch<Dispatch>();
  const useInfo = useSelector((state: RootModel) => state.home.useInfo);  

  useEffect(() => {
    // getUserInfo().then((res) => {
    //   dispatch.home.setUseInfo(res);
    // });
    dispatch.home.getUserInfo2();
  }, []);
  
  return (
    <div>
      <h1>Home</h1>
      <p>useInfo: {JSON.stringify(useInfo?.name)}</p>
      <p>useInfo: {JSON.stringify(useInfo?.age)}</p>
    </div>
  );
};

export default Home;

当前聪明的你可能会想到将页面中的所有接口都写在effects里面,然后在homeModel文件中统一管理所有接口的数据状态,你也许可以尝试尝试

踩踩坑

本期暂无小坑~

总结

好了,以上就是我们状态管理相关的内容,希望对你有所帮助~

相关推荐
是一个Bug8 小时前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu121388 小时前
React面向组件编程
开发语言·前端·javascript
持续升级打怪中8 小时前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路8 小时前
GDAL 实现矢量合并
前端
hxjhnct8 小时前
React useContext的缺陷
前端·react.js·前端框架
冰暮流星8 小时前
javascript逻辑运算符
开发语言·javascript·ecmascript
前端 贾公子8 小时前
从入门到实践:前端 Monorepo 工程化实战(4)
前端
菩提小狗8 小时前
Sqlmap双击运行脚本,双击直接打开。
前端·笔记·安全·web安全
前端工作日常9 小时前
我学习到的AG-UI的概念
前端
韩师傅9 小时前
前端开发消亡史:AI也无法掩盖没有设计创造力的真相
前端·人工智能·后端