HOW - React 状态模块化管理和按需加载(一) - react-redux

devtools/2024/11/30 15:23:39/

目录

  • 一、背景
  • 二、react-redux
    • 模块化管理
      • 1. 模块化文件结构
      • 2. 使用 Redux Toolkit 的 Slice
        • 例子:用户模块 (userSlice)
        • 例子:商品模块 (productSlice)
      • 3. 合并 Reducers
      • 4. 配置 Store
      • 5. 使用 Redux 状态和操作
        • 例子:获取用户信息
      • 6. 拓展
    • 按需加载
      • 1. 动态模块加载的核心逻辑
      • 2. 实现动态加载 Reducer
        • 修改 Store 配置
      • 3. 动态注入模块的 Reducer
        • 示例:动态加载 `user` 模块
        • 示例:动态加载 `product` 模块
      • 4. 组件中按需加载模块
        • 示例:加载 `user` 模块
      • 5. 结合代码分割
        • 示例:使用 React 的 `lazy` 和 `Suspense`
      • 6. 清除不需要的模块
        • 实现移除 Reducer 的方法
        • 示例:移除 `user` 模块
      • 7. 优势

一、背景

在构建 React 项目时,必不可少的就是一些全局或者多组件共享数据的管理。官方推荐使用 react-redux.

但现在还流行另外一个框架,jotai.

我们今天就来学习一下如何基于 react-redux进行状态模块化管理和按需加载,包括为什么且在什么场景下可以引入 jotai 使用。

redux_9">二、react-redux

关于 react-redux 基本使用就不过多介绍了。

模块化管理

在 React + Redux 项目中,将 Redux 状态和逻辑分模块管理是一种常见的实践,可以提高代码的可维护性和可扩展性。以下是分模块管理 Redux 的常用方法和步骤:

1. 模块化文件结构

一种常见的项目结构是将每个功能模块的状态管理放在单独的文件夹中。
例如,一个项目包含用户管理和商品管理模块,可以按如下结构组织:

src/
├── store/          // Redux 全局 store
│   ├── index.ts    // 配置 store
│   ├── rootReducer.ts // 合并 reducers
├── features/
│   ├── user/       // 用户模块
│   │   ├── userSlice.ts
│   │   ├── userActions.ts
│   │   ├── userSelectors.ts
│   ├── product/    // 商品模块
│   │   ├── productSlice.ts
│   │   ├── productActions.ts
│   │   ├── productSelectors.ts

2. 使用 Redux Toolkit 的 Slice

Redux Toolkit 提供了 createSlice 方法,可以更轻松地管理每个模块的状态和操作。

例子:用户模块 (userSlice)
// src/features/user/userSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';interface UserState {userInfo: { id: string; name: string } | null;isLoading: boolean;
}const initialState: UserState = {userInfo: null,isLoading: false,
};const userSlice = createSlice({name: 'user',initialState,reducers: {setUserInfo(state, action: PayloadAction<{ id: string; name: string }>) {state.userInfo = action.payload;},clearUserInfo(state) {state.userInfo = null;},setLoading(state, action: PayloadAction<boolean>) {state.isLoading = action.payload;},},
});export const { setUserInfo, clearUserInfo, setLoading } = userSlice.actions;
export default userSlice.reducer;
例子:商品模块 (productSlice)
// src/features/product/productSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';interface Product {id: string;name: string;price: number;
}interface ProductState {productList: Product[];isLoading: boolean;
}const initialState: ProductState = {productList: [],isLoading: false,
};const productSlice = createSlice({name: 'product',initialState,reducers: {setProducts(state, action: PayloadAction<Product[]>) {state.productList = action.payload;},addProduct(state, action: PayloadAction<Product>) {state.productList.push(action.payload);},setLoading(state, action: PayloadAction<boolean>) {state.isLoading = action.payload;},},
});export const { setProducts, addProduct, setLoading } = productSlice.actions;
export default productSlice.reducer;

3. 合并 Reducers

使用 Redux Toolkit 的 combineReducers 将模块化的 reducer 合并。

// src/store/rootReducer.ts
import { combineReducers } from '@reduxjs/toolkit';
import userReducer from '../features/user/userSlice';
import productReducer from '../features/product/productSlice';const rootReducer = combineReducers({user: userReducer,product: productReducer,
});export default rootReducer;

4. 配置 Store

在 Redux Toolkit 中,通过 configureStore 来配置全局 Store。

// src/store/index.ts
import { configureStore } from '@reduxjs/toolkit';
import rootReducer from './rootReducer';const store = configureStore({reducer: rootReducer,
});export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;export default store;

5. 使用 Redux 状态和操作

在组件中通过 useSelectoruseDispatch 使用 Redux 状态和操作。

例子:获取用户信息
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { RootState } from '../../store';
import { setUserInfo } from '../../features/user/userSlice';const UserProfile: React.FC = () => {const userInfo = useSelector((state: RootState) => state.user.userInfo);const dispatch = useDispatch();const handleLogin = () => {dispatch(setUserInfo({ id: '1', name: 'John Doe' }));};return (<div>{userInfo ? (<p>Welcome, {userInfo.name}</p>) : (<p>Please log in</p>)}<button onClick={handleLogin}>Log In</button></div>);
};export default UserProfile;

6. 拓展

  • 异步操作:使用 Redux Toolkit 的 createAsyncThunk 管理异步逻辑。
  • 代码分割:结合动态加载模块,按需加载 Redux 状态和逻辑。
  • 测试:为每个模块单独编写单元测试,提高代码稳定性。

这种分模块的方式可以帮助你清晰地组织 Redux 状态,增强项目的可维护性。

按需加载

在某些场景下,只需要多个模块之间共享一份数据,即这份数据并不是 global 全局的。

则可以通过 按需加载 Redux 模块 来实现内存中只加载需要的模块,从而优化性能和内存占用。

这通常结合 动态模块加载 和 Redux 的 replaceReducer 功能来实现。

以下是如何实现按需加载 Redux 模块的完整方案:

1. 动态模块加载的核心逻辑

Redux 支持使用 store.replaceReducer 替换当前的根 reducer,这样就可以在需要时动态添加新的模块。

2. 实现动态加载 Reducer

修改 Store 配置

首先,创建一个支持动态加载的 Store。

// src/store/index.ts
import { configureStore, Reducer, CombinedState } from '@reduxjs/toolkit';
import { combineReducers } from 'redux';type AsyncReducers = {[key: string]: Reducer;
};const createRootReducer = (asyncReducers: AsyncReducers) =>combineReducers({// 这里可以放一些全局的静态 reducer...asyncReducers,});const store = configureStore({reducer: createRootReducer({}),
});type RootState = ReturnType<typeof store.getState>;
type AppDispatch = typeof store.dispatch;store.asyncReducers = {} as AsyncReducers;store.injectReducer = (key: string, reducer: Reducer) => {if (!store.asyncReducers[key]) {store.asyncReducers[key] = reducer;store.replaceReducer(createRootReducer(store.asyncReducers));}
};export { RootState, AppDispatch };
export default store;

说明:

  • store.asyncReducers 用于存储动态加载的 reducers。
  • store.injectReducer 是动态注入 reducer 的方法。

3. 动态注入模块的 Reducer

在需要时加载模块并动态注入其 reducer。

示例:动态加载 user 模块
// src/features/user/index.ts
import userReducer from './userSlice';
import store from '../../store';store.injectReducer('user', userReducer);
示例:动态加载 product 模块
// src/features/product/index.ts
import productReducer from './productSlice';
import store from '../../store';store.injectReducer('product', productReducer);

4. 组件中按需加载模块

在组件中使用模块时,可以按需加载 reducer。

示例:加载 user 模块
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { RootState } from '../../store';
import { setUserInfo } from './userSlice';// 动态加载 user 模块
import('./index');const UserProfile: React.FC = () => {const userInfo = useSelector((state: RootState) => state.user?.userInfo);const dispatch = useDispatch();useEffect(() => {if (!userInfo) {dispatch(setUserInfo({ id: '1', name: 'John Doe' }));}}, [dispatch, userInfo]);return <div>Welcome, {userInfo?.name || 'Guest'}</div>;
};export default UserProfile;

说明

  • import('./index') 动态加载 user 模块并注入 reducer。
  • 使用时无需提前加载其他模块,减少内存占用。

5. 结合代码分割

使用 Webpack 或 Vite 的代码分割功能,将每个模块单独打包,按需加载。

示例:使用 React 的 lazySuspense
import React, { Suspense } from 'react';const UserProfile = React.lazy(() => import('./features/user/UserProfile'));const App: React.FC = () => {return (<Suspense fallback={<div>Loading...</div>}><UserProfile /></Suspense>);
};export default App;

效果

  • UserProfile 页面访问时,动态加载 user 模块。
  • 内存中仅保留使用过的模块(如 user),未使用的模块(如 product)不会加载。

6. 清除不需要的模块

如果模块加载后不再需要,可以从内存中移除 reducer。

实现移除 Reducer 的方法
store.removeReducer = (key: string) => {if (store.asyncReducers[key]) {delete store.asyncReducers[key];store.replaceReducer(createRootReducer(store.asyncReducers));}
};
示例:移除 user 模块
store.removeReducer('user');

7. 优势

  • 减少内存占用:未使用的模块不会加载到内存中。
  • 优化首屏性能:只加载当前页面需要的模块。
  • 代码分离:结合动态加载和代码分割,实现更清晰的模块管理。

这种按需加载 Redux 模块的方式非常适合大型项目,尤其是模块众多但同时使用的模块有限的场景。


http://www.ppmy.cn/devtools/138232.html

相关文章

net9 abp vnext 多语言通过数据库动态管理

通过数据库加载实现动态管理&#xff0c;用户可以自己修改界面显示的文本&#xff0c;满足国际化需求 如图所示,前端使用tdesign vnext 新建表TSYS_Localization与TSYS_LocalizationDetail 国旗图标下载网址flag-icons: Free Country Flags in SVG 在Shared下创建下图3个文件 …

VUE前端实现天爱滑块验证码--详细教程

第一步&#xff1a; Git地址&#xff1a;tianai-captcha-demo: 滑块验证码demo 找到目录 src/main/resources/static,拷贝 static 并改名为 tac 即可。 第二步&#xff1a; 将改为 tac 的文件&#xff0c;放进项目根目录中&#xff0c;如下图&#xff1a; 第三步&#xff1…

【RAG多模态】mR^2AG:基于知识的多模态检索-反思增强生成方法浅尝

在基于知识的视觉问答&#xff08;Knowledge-based VQA&#xff09;任务中&#xff0c;输入通常是一对图像和问题 ( I , Q ) (I, Q) (I,Q)&#xff0c;并且依赖于可访问的知识库。文章提出的 m R 2 A G mR^2AG mR2AG框架通过两个新颖的反思操作来解耦生成过程&#xff0c;从而…

解决水库安全监测难题 长期无外接电源 低功耗设备智能化监测系统

解决水库安全监测难题 长期无外接电源 低功耗设备智能化监测系统 国内某水库安全监测项目需要监测点分散&#xff0c;且无外接供电。项目年限为4年&#xff0c;不允许使用太阳能电板。因此&#xff0c;我们需要设备具备低功耗且内置电池的功能。为了满足客户的要求&#xff0c;…

TiDB 架构

整体架构 与传统的单机数据库相比&#xff0c;TiDB 具有以下优势&#xff1a; 纯分布式架构&#xff0c;拥有良好的扩展性&#xff0c;支持弹性的扩缩容支持 SQL&#xff0c;对外暴露 MySQL 的网络协议&#xff0c;并兼容大多数 MySQL 的语法&#xff0c;在大多数场景下可以直…

k8s 1.28 聚合层部署信息记录

–requestheader-client-ca-file –requestheader-allowed-namesfront-proxy-client –requestheader-extra-headers-prefixX-Remote-Extra- –requestheader-group-headersX-Remote-Group –requestheader-username-headersX-Remote-User –proxy-client-cert-file –proxy-cl…

初识Hive

初识Hive 什么是 Hive&#xff1f; Hive 是基于 Hadoop 的数据仓库工具&#xff0c;用于处理存储在 Hadoop 上的结构化和半结构化数据。Hive 可以让用户通过 SQL 类似的语言&#xff08;HiveQL&#xff09;来查询和分析数据&#xff0c;而不需要了解复杂的 MapReduce 编程模型…

Tomcat(39)如何在Tomcat中配置SSL会话缓存?

在Tomcat中配置SSL会话缓存是优化SSL/TLS性能的一个重要步骤&#xff0c;尤其是在处理大量并发SSL连接时。通过会话缓存&#xff0c;可以避免重复的SSL握手过程&#xff0c;从而减少CPU使用和提高响应速度。以下是在Tomcat中配置SSL会话缓存的详细步骤。 1. 配置server.xml S…