【REACT-@reduxjs/toolkit+react-redux+redux-persist状态管理】

news2024/12/25 15:03:19

REACT-@reduxjs/toolkit+react-redux+redux-persist状态管理

  • 1. 依赖包安装
  • 2. 目录结构
  • 3. 修改Index.js
  • 4. createSlice()
    • 4.1 action处理
      • 4.1.1 创建collapsedSlice
      • 4.1.2 使用collapsedSlice
    • 4.2 异步action处理
      • 4.2.1 使用redux-thunk方式处理异步
        • 4.2.1.1 创建asyncReduxSlice
        • 4.2.1.2 使用asyncReduxSlice
      • 4.2.2 createAsyncThunk()支持thunk
      • 4.2.2.1 创建CATSlice
      • 4.2.2.1 使用CATSlice
  • 5. Selector使用缓存
    • 5.1 创建listFilterSlice
    • 5.1 使用listFilterSlice
  • 6. 调试工具

参考:
Redux最新实践指南之Redux-Toolkit
redux最佳实践redux-toolkit使用指南
react+ts实战之 @reduxjs/toolkit
2015年发布的Redux至今仍是React生态中最常用的状态管理库,2022年4月19日发布的Redux v4.2.0中正式将createStore方法标记为“已弃用”(@deprecated),并推荐用户使用Redux-Toolkit(下面简称为RTK)的configureStore方法。

1. 依赖包安装

npm i @reduxjs/toolkit react-redux redux-persist

2. 目录结构

在这里插入图片描述

3. 修改Index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import "./axios";
import { Provider } from 'react-redux';
import store,{persistor} from './redux/store'
import { PersistGate } from "redux-persist/integration/react";
// import reportWebVitals from './reportWebVitals';
// npm i -g json-server
// json-server --watch ./db.json --port 5000
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  // <React.StrictMode>
  <Provider store={store}>
    <PersistGate loading={null} persistor={persistor}>
      <App />
    </PersistGate>
  </Provider>
  // </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
// reportWebVitals();

4. createSlice()

使用createSlice方法创建一个slice。每一个slice里面包含了reducer和actions,可以实现模块化的封装。所有的相关操作都独立在一个文件中完成。

4.1 action处理

4.1.1 创建collapsedSlice

CollapsedSlice.js

import { createSlice } from '@reduxjs/toolkit';

const initialState = {
  collapsed:false
}

export const collapsedSlice = createSlice({
  // 命名空间,在调用action的时候会默认的设置为action的前缀collapsedSlice/changeCollapsed
  name:'collapsedSlice',
  // 初始值
  initialState:initialState,
  // 这里的属性会自动的导出为collapsedSlice.actions,在组件中可以直接通过dispatch进行触发
  reducers:{
    //{ payload }解构出来的payload是dispatch传递的数据对象
    changeCollapsed(state,action){
      // console.log(action)
      // {
      //   "type":"collapsedSlice/changeCollapsed",
      //   "payload":{
      //       "value":2
      //   }
      // }

      // 内置了immutable不可变对象来管理state,不用再自己拷贝数据进行处理
      state.collapsed = !state.collapsed;
    }
  }
})

// 导出actions
export const { changeCollapsed } = collapsedSlice.actions;

// 导出reducer,在创建store时使用到
export default collapsedSlice.reducer;

4.1.2 使用collapsedSlice


import { useSelector, useDispatch } from 'react-redux';
import {changeCollapsed} from '../../redux/features/CollapsedSlice' // 引入actions

import {
  MenuUnfoldOutlined,
  MenuFoldOutlined,
  UserOutlined,
} from "@ant-design/icons";
import React from 'react'
import { Layout, Dropdown, Avatar,Button } from "antd";
import { useNavigate } from 'react-router';
const { Header} = Layout;

export default function TopHeader() {
  //根据store.js中设置的reducer名字,从CollapsedSlice空间获取state
  const {collapsed} = useSelector(state=>state.CollapsedSlice);
  const dispatch = useDispatch();

  const handleCollapsed = ()=>{
    dispatch(changeCollapsed({value:2}));
  }

  const { username, role:{roleName} } = JSON.parse(localStorage.getItem("token"));
  const navigate = useNavigate();
  const items = [
    {
      key: '1',
      label: roleName,
    },
    {
      key: '2',
      label: (
        <Button type="primary" danger 
          onClick={() => {
            localStorage.removeItem("token");
            navigate("/login",{replace:true}); 
          }}
        >
          退出登录
        </Button> 
      ),
    },
  ];
  
  return (
    <Header className="site-layout-background" style={{ paddingLeft: "16px" }}>
      {/* {React.createElement(collapsed ? MenuUnfoldOutlined : MenuFoldOutlined, {
        className: 'trigger',
        onClick: () => setCollapsed(!collapsed),
      })} */}

      {
        collapsed ? <MenuUnfoldOutlined  onClick={handleCollapsed}/>: <MenuFoldOutlined onClick={handleCollapsed}/>
      }
      <div style={{ display: "inline", float: "right" }}>
        <span>
          欢迎<span style={{ color: "#1890ff" }}>{username}</span>回来
        </span>
        <Dropdown  menu={{items}}>
          <div style={{display:'inline-block',cursor:'pointer'}} onClick={(e) => e.preventDefault()}>
            <Avatar size="large" icon={<UserOutlined />} />
          </div>
        </Dropdown>
      </div>
    </Header>
  )
}

4.2 异步action处理

4.2.1 使用redux-thunk方式处理异步

RTK集成了redux-thunk来处理异步事件,所以可以按照之前thunk的写法来写异步请求

4.2.1.1 创建asyncReduxSlice

AsyncReduxSlice.js

import { createSlice } from "@reduxjs/toolkit";
import axios from 'axios'

const initialState = {
  list:[]
}
export const asyncReduxSlice = createSlice({
  name:'asyncReduxSlice',
  initialState,
  reducers:{
    changeList(state,{payload}){
      state.list = payload.list;
    }
  }
})

const {changeList} = asyncReduxSlice.actions;

export const getList = payload => {
  return dispatch => {
    axios.get("/rights?_embed=children").then((res) => { 
      dispatch(changeList({list:res.data}))
    });
  }
}

export default asyncReduxSlice.reducer;

4.2.1.2 使用asyncReduxSlice

Test-AsyncRedux.js

import React, { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux/es/exports'
import {getList} from '../redux/features/AsyncReduxSlice'

export default function TestAsyncRedux() {
  const {list} = useSelector(state=>state.AsyncReduxSlice);
  const dispatch = useDispatch();

  useEffect(()=>{
    console.log(111111)
    if(list.length === 0){
      dispatch(getList())
    }else{
      alert('列表已被缓存!')
    }
  },[dispatch,list.length]);

  return (
    <div>
      {
        list.map(item=>{
          return <li key={item.id}>{item.title}</li>
        })
      }
    </div>
  )
}

4.2.2 createAsyncThunk()支持thunk

createAsyncThunk方法可以创建一个异步的action,这个方法被执行的时候会有三个( pending(进行中) fulfilled(成功) rejected(失败))状态。可以监听状态的改变执行不同的操作。以下代码示例中使用到了extraReducers创建额外的action对数据获取的状态信息进行监听。

4.2.2.1 创建CATSlice

CreateAsyncThunkSlice.js

import {createSlice,createAsyncThunk} from '@reduxjs/toolkit'
import axios from 'axios'

const getListAPI = ()=>{
  return axios.get("/rights?_embed=children").then((res) => res.data);
}

export const loadList = createAsyncThunk(
  'CATSlice/loadList',
  async ()=>{
    const list = await getListAPI();
    return list;
  }
)
const initialState = {
  list:[]
}
export const CATSlice = createSlice({
  name:'CATSlice',
  initialState,
  // 可以额外的触发其他slice中的数据关联改变
  extraReducers: builder=>{
    builder
      .addCase(loadList.pending,state=>{
        console.log('进行中');
      })
      .addCase(loadList.fulfilled,(state,action)=>{
        console.log('成功');
        const { payload } = action;
        // {
        //   type:'CATSlice/loadList/fulfilled',
        //   payload://loadList返回值
        // }
        console.log(action);
        state.list = payload;
      })
      .addCase(loadList.rejected,(state, err)=>{
        console.log('失败',err);
      })
 }
})

export default CATSlice.reducer;

4.2.2.1 使用CATSlice

Test-CreateAsyncThunk.js

import React, { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux/es/exports'
import {loadList} from '../redux/features/CreateAsyncThunkSlice'

export default function TestAsyncRedux() {
  const {list} = useSelector(state=>{
    console.log(state)
    return state.CreateAsyncThunkSlice
  });
  const dispatch = useDispatch();

  useEffect(()=>{
    console.log(11111111)
    if(list.length === 0){
      dispatch(loadList())
    }else{
      alert('列表已被缓存!')
    }
  },[]);// eslint-disable-line

  return (
    <div>
      {
        list.map(item=>{
          return <li key={item.id}>{item.title}</li>
        })
      }
    </div>
  )
}

5. Selector使用缓存

当useSelector方法涉及到复杂逻辑运算时,且返回一个对象的时候,每次运行都返回了一个新的引用值,会使组件重新渲染,即使返回的数据内容并没有改变
为了解决这个问题,可以使用Reselect库,它是一个创建记忆化selector的库,只有在输入发生变化时才会重新计算结果,rtk正是集成了这个库,并把它导出为createSelector函数

引用的CreateAsyncThunkSlice.js参照上面

5.1 创建listFilterSlice

ListFilterSlice.js

import { createSlice } from '@reduxjs/toolkit';

const initialState = {
  listFilter:'/home'
}

export const listFilterSlice = createSlice({
  // 命名空间,在调用action的时候会默认的设置为action的前缀listFilterSlice/changelistFilter
  name:'listFilterSlice',
  // 初始值
  initialState:initialState,
  // 这里的属性会自动的导出为listFilterSlice.actions,在组件中可以直接通过dispatch进行触发
  reducers:{
    //{ payload }解构出来的payload是dispatch传递的数据对象
    changelistFilter(state,action){
      // console.log(action)
      // {
      //   "type":"listFilterSlice/changelistFilter",
      //   "payload":{
      //       "value":2
      //   }
      // }

      // 内置了immutable不可变对象来管理state,不用再自己拷贝数据进行处理
      state.listFilter = !state.listFilter;
    }
  }
})

// 导出actions
export const { changelistFilter } = listFilterSlice.actions;

// 导出reducer,在创建store时使用到
export default listFilterSlice.reducer;

5.1 使用listFilterSlice

Test-CreateSelector.js

import React, { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux/es/exports'
import {loadList} from '../redux/features/CreateAsyncThunkSlice'
import { createSelector } from '@reduxjs/toolkit';

//当useSelector方法涉及到复杂逻辑运算时,且返回一个对象的时候,每次运行都返回了一个新的引用值,会使组件重新渲染,即使返回的数据内容并没有改变
//为了解决这个问题,可以使用Reselect库,它是一个创建记忆化selector的库,只有在输入发生变化时才会重新计算结果,rtk正是集成了这个库,并把它导出为createSelector函数
const selectList = state=>state.CreateAsyncThunkSlice;
const selectFilter = state=>state.ListFilterSlice;

const filterList = createSelector(selectList,selectFilter,(listState,filterState)=>{
  const {list} = listState;
  const {listFilter} = filterState;
  console.log(listState,filterState)
  switch(listFilter){
    case 'all':
      return list;
    case '/home':
      return list.filter(item=>item.key === listFilter);
    default :
      throw new Error('Unknown filter: ' + listFilter);
  }
})

export default function TestAsyncRedux() {
  const mapList = useSelector(state => filterList(state));
  const dispatch = useDispatch();

  useEffect(()=>{
    console.log(11111111)
    if(mapList.length === 0){
      dispatch(loadList())
    }else{
      alert('列表已被缓存!')
    }
  },[]);// eslint-disable-line

  return (
    <div>
      {
        mapList.map(item=>{
          return <li key={item.id}>{item.title}</li>
        })
      }
    </div>
  )
}

6. 调试工具

RTK中已经配置了redux-devtools,所以只要浏览器安装调试工具redux-devtools-extension
安装参考redux-devtools-extension

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/374600.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java+MySQL汽车租赁管理系统课程设计

wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;汽车租赁系统 获取完整源码源文件视频演示文档资料等 一、 课程设计目的 1、综合运用Java程序设计课程和其他相关课程的理论和知识&#xff0c;掌握面向对象程序设计的一般方法、常用技术及技巧&#xff0c;树立良好的软件…

MongoDB安装(新版本保姆级教程)

前言MongoDB 是一个文档数据库&#xff0c;旨在简化开发和扩展。本篇文章介绍MongoDB 数据库及其 可视化工具 MongoDB Compass 的详细教程 (window10操作系统)下载安装包首先进入官网(社区版) &#xff0c;在对应页面选择需要安装的版本 (这里下载当前适合版本号)传送门安装因为…

CRI-O, Containerd, Docker, Postman等概念介绍

参考&#xff1a;Docker&#xff0c;containerd&#xff0c;CRI&#xff0c;CRI-O&#xff0c;OCI&#xff0c;runc 分不清&#xff1f;看这一篇就够了Docker, containerd, CRI-O and runc之间的区别&#xff1f; Docker、Podman、Containerd 谁才是真正王者&#xff1f;CRI-O …

最简单的代码生成器,smartsofthelp netframework ,EF 架构

1.原生 sql操作公共类 dbhelper2.model 带注释的实体3.EF功能访问调用类3.EFData EF数据接口层4.UI展示层数据库脚本 自动生成Model /// <summary>/// Model实体层 /// </summary>namespace Smart.Model{/// <summary>/// 数据实体层 T_Eventsmart 投屏事件/…

CI/CD | 深入研究Jenkins后,我挖掘出了找到了摆脱低效率低下的方法

在本系列的第一篇文章中&#xff0c;您已经了解了一些关于如何管理Jenkins的内容&#xff0c;主要是为无序的人带来秩序。在这篇文章中&#xff0c;我将更深入地探讨我效率低下的问题&#xff0c;提出我们工作流中一些安全性、治理和合规性的挑战。这不仅仅是你在网站上或展览横…

从此不怕被盗号:教你如何用 Python 制作一个密码生成器

原由&#xff1a; 定期更换密码是一种非常重要的安全措施&#xff0c;这种做法可以有效地保护你的账户和个人信息不受黑客和网络攻击者的侵害。 密码泄露是一个非常普遍的问题&#xff0c;许多人的账户和密码经常会被泄露出来&#xff0c;导致个人信息被盗用、金融损失、恶意…

hive建分区表,分桶表,内部表,外部表

hive建分区表&#xff0c;分桶表&#xff0c;内部表&#xff0c;外部表 一、概念介绍 Hive是基于Hadoop的一个工具&#xff0c;用来帮助不熟悉 MapReduce的人使用SQL对存储在Hadoop中的大规模数据进行数据提取、转化、加载。Hive数据仓库工具能将结构化的数据文件映射为一张数…

Redis 如何实现库存扣减操作和防止被超卖

在日常开发中有很多地方都有类似扣减库存的操作&#xff0c;比如电商系统中的商品库存&#xff0c;抽奖系统中的奖品库存等。其基本的流程如下:1 解决方案使用mysql数据库&#xff0c;使用一个字段来存储库存&#xff0c;每次扣减库存去更新这个字段。还是使用数据库&#xff0…

自动视觉锁螺丝机及其控制系统

1.全自动智能锁螺丝机的意义电子消耗品、家用电器、汽车零件、音箱、安防、玩具、LED 等制造业的生产过程中&#xff0c;产品组装是一个非常重要的步骤&#xff0c;而螺丝锁付是组装过程的核心工艺之一。当下国内智能手机、智能汽车、智能家电等产品更新换代快&#xff0c;产品…

深入理解C#的协变和逆变及其限制原因

阅读本文需要的一些前置知识&#xff1a; C#基本语法、C#的泛型使用、C#的运行过程 由于协变和逆变存在一些细节&#xff0c;在阅读时请注意“接口”和“类型”的差异&#xff0c;此外&#xff0c;文中有可能在不同的语境中将“结构体”和“值类型”混用&#xff0c;但表达的同…

JavaEE简单实例——MyBatis一对多关联映射的嵌套结果集查询

简单介绍&#xff1a; 在之前的章节&#xff0c;我们简单介绍了MyBatis中的一对一的关联查询&#xff0c;使用了嵌套查询和嵌套结果集两种方式进行讲解&#xff0c;但是在实际的使用中&#xff0c;我们常用的是嵌套结果集的查询方式&#xff0c;所以在一对多的查询中&#xff…

无线耳机哪个品牌音质好?2023无线蓝牙耳机音质排行

现今&#xff0c;外出佩戴蓝牙耳机的人越来越多&#xff0c;各大品牌厂商对于蓝牙耳机各种性能的设计也愈发用心。那么&#xff0c;无线耳机哪个品牌音质好&#xff1f;下面&#xff0c;我来给大家推荐几款音质好的无线蓝牙耳机&#xff0c;可以当个参考。 一、南卡小音舱蓝牙…

【人工智能】科大讯飞API接口调用(第一集)

前言 这学期有一门人工智能教育课程&#xff0c;恰巧又有这么一个实践&#xff0c;要求进行人工智能接口调用 于是首选了科大讯飞&#xff0c;下面是详细过程记录 科大讯飞接口调用 以下是流程以及实现细节描述 调用流程 第一步 来到科大讯飞开放平台 http://www.xfyun.…

四、阻塞队列

文章目录基础概念生产者消费者概念JUC阻塞队列的存取方法ArrayBlockingQueueArrayBlockingQueue的基本使用生产者方法实现原理ArrayBlockingQueue的常见属性add方法实现offer方法实现offer(time,unit)方法put方法消费者方法实现原理remove方法poll方法poll(time,unit)方法take方…

wpf -绑定

事件双向驱动滑块变化&#xff0c;将值赋给文本控件与控件之间双向绑定{Binding ElementNameslider, PathValue}ElementName: 绑定哪个控件呢&#xff1f; 指绑定的x:Name这个控件Path &#xff1a; 绑定哪个属性呢&#xff1f;Value<StackPanel><Slider x:Name"…

Python每日一练(20230227)

目录 1. 路径交叉 ★★★ 2. 缺失的第一个正数 ★★★ 3. 寻找两个正序数组的中位数 ★★★ 附录 散列表 基本概念 常用方法 1. 路径交叉 给你一个整数数组 distance 。 从 X-Y 平面上的点 (0,0) 开始&#xff0c;先向北移动 distance[0] 米&#xff0c;然后向西移…

ChatGPT提高你日常工作的五个特点,以及如何使用它来提高代码质量

ChatGPT已经完全改变了代码开发模式。然而&#xff0c;大多数软件开发者和数据专家们仍然不使用ChatGPT来完善——并简化他们的工作。 这就是我们在这里列出提升日常工作效率和质量的5个不同的特点的原因。 让我们一起来看看在日常工作中如何使用他们。 警告&#xff1a;不要…

第十一届“泰迪杯”数据挖掘挑战赛赛前指导安排

第十一届“泰迪杯”挑战赛报名一周了&#xff0c;许多的参赛队伍及带队老师都在咨询我们赛前指导安排及内容&#xff0c;今年的赛前指导安排还是分为了赛前指导录播课程及赛前指导直播两个模块。小编这就为大家介绍一下吧。 赛前指导 赛前指导录播课程 2月25日9:00-4月14日 …

vue中的百度地图的搜索定位功能

效果图 申请百度地图AK 前往 百度地图开放平台控制台 &#xff0c;登录百度账号&#xff0c;创建应用即得。 封装loadBMap.js文件 /*** 动态加载百度地图api函数* param {String} ak 百度地图AK&#xff0c;必传*/ export default function loadBMap(ak) {return new Promise…

C语言|文件读写,代码运行后留下“记忆”

前言对于一个代码&#xff0c;运行时可能需要保留产生的结果&#xff0c;例如计算值&#xff0c;筛选值&#xff0c;记录点或者小游戏的得分&#xff0c;而正常情况下我们要保存一个数据&#xff0c;想到的肯定是打开我们的文本软件&#xff0c;手撸文字&#xff0c;今天这篇文…