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文件中统一管理所有接口的数据状态,你也许可以尝试尝试

踩踩坑

本期暂无小坑~

总结

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

相关推荐
By北阳2 小时前
Less resolver error:‘~antd/es/style/themes/index.less‘ wasn‘t found.
前端·elasticsearch·less
西洼工作室2 小时前
SSE与轮询技术实时对比演示
前端·javascript·css
IT_陈寒4 小时前
Vite 5.0 性能优化实战:3 个关键配置让你的构建速度提升50%
前端·人工智能·后端
excel4 小时前
Vue2 动态添加属性导致页面不更新的原因与解决方案
前端
GISer_Jing7 小时前
明天好好总结汇总分析博客
前端·javascript·面试
做运维的阿瑞9 小时前
Windows 环境下安装 Node.js 和 Vue.js 框架完全指南
前端·javascript·vue.js·windows·node.js
Dontla11 小时前
Tailwind CSS介绍(现代CSS框架,与传统CSS框架Bootstrap对比)Tailwind介绍
前端·css·bootstrap
yinuo11 小时前
uniapp微信小程序安卓手机Touchend事件无法触发
前端
你的人类朋友13 小时前
【Node】Node.js 多进程与多线程:Cluster 与 Worker Threads 入门
前端·后端·node.js