React:Redux

news/2025/3/6 14:39:24/

Redux引入

Redux感觉像组件通信的中介

state存放被管理的状态

action是申请改哪些数据,传入什么参数

reducer是怎么修改数据

我的理解更像是action像一个储存方法的对象,reducer是具体的方法的实现,不同的方法实现也不一样

store是个仓库,存储应用状态的仓库

自己实现redux对状态的管理

首先进行初始化:

javascript">import { createSlice } from '@reduxjs/toolkit';createSlice({name: 'counter', // slice 的名称initialState: {count: 0, // 初始状态},
});

初始化是对slice的初始化,也就是切片,切片的名字叫counter,初始状态为{counter:0}

有了状态以后设置修改状态的方法

javascript">import { createSlice } from '@reduxjs/toolkit'
const counterStore=createSlice({//初始化name: 'counter',initialState: {count:0 },//编写修改数据的方法,同步方法,支持直接修改reducers: {increment(state) {state.count++},decrement(state) {state.count--}}
}) 

解构获取这两个方法:

javascript">//解构actionCreater函数
const { increment, decrement } = counterStore.actions

获取reducer:

javascript">//获取reducer
const reducer = counterStore.reducer

按需导出reducer和action creators,导出actioncreators是为了派发action,触发状态的更新

reducer在Redux store里处理action,更新状态

javascript">//按需导出actionCreater
export { increment, decrement }
//默认导出的方式导出reducer
export default reducer

然后在index.js文件里组合

configureStore是 Redux Toolkit 提供的一个函数,用于创建 Redux store

javascript">import { configureStore } from "@reduxjs/toolkit";

counter Reducer是从 ./modules/counterStore 文件中导入的 reducer

javascript">import { configureStore } from "@reduxjs/toolkit";
//导入子模块reducer
import counterReducer from './modules/counterStore'

这里的reducer是个对象,将多个子模块的reducer组合在一起

counter是模块的名称,counterReducer是模块的reducer

由configureStore创建Redux store,支持异步操作(不过这里写的都是同步)

javascript">import { configureStore } from "@reduxjs/toolkit";
//导入子模块reducer
import counterReducer from './modules/counterStore'
configureStore({reducer: {counter:counterReducer
}
})

然后导出store,在组件里使用

在组件里使用需要用中间件链接,这步叫为react注入store:

javascript">import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import reportWebVitals from './reportWebVitals';
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import { Provider } from 'react-redux';
import store from './store'const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<React.StrictMode><Provider store={store}>{/* //注入 */}<App/></Provider></React.StrictMode>
)

要使用store里的数据,需要用到一个钩子函数useSelector

在App组件里调用useSelector

javascript">import { useSelector } from "react-redux";
function App() {const {count}=useSelector(state=>state.counter)return (<div className='App'>
{count}</div>)
}
export default App

然后在index.js引入App并调用就好了

一些注意事项

默认导出的文件应该用默认导入的文件引入:

javascript">//默认导出/导入
export default store
import store from './store'

这叫命名导出/导入:

javascript">//命名导出
const name = 'John';
function greet() {console.log('Hello!');
}export { name, greet }; // 导出多个内容
//命名导入
import { name, greet } from './module';

如果你认为你的代码和配置完全正确,但是还是报错(我就这样),那可能是webpack把错误缓存下来了,需要清理webpack:

javascript">//删除package-lock.json
rm -rf node_modules package-lock.json
//重新下载
npm install
//然后启动
npm start

react组件里修改store的数据

引入App.js

useDispatch是封装好的钩子,dispatch方法调用的时候把一个dispatch赋值给这个形参

javascript">import { useDispatch, useSelector } from "react-redux";
//导入actionCreater
import {increment,decrement} from './store/modules/counterStore'
function App() {const { count } = useSelector(state => state.counter)const dispatch=useDispatch()return (<div className='App'><button onClick={()=>dispatch(decrement())}>-</button>{count}<button onClick={()=>dispatch(increment())}>+</button></div>)
}
export default App  

这样就可以控制数字的+-了

actions提交参数

修改reduer,将state.count更新为action.payload的值

javascript">  reducers: {increment(state) {state.count++},decrement(state) {state.count--},addToNum(state,action) {state.count=action.payload}}

在App.js里添加一个按钮,并传入参数:

javascript"> <button onClick={() => dispatch(addToNum(0))}>

异步状态
 

很遗憾我没有实现出来,因为远程axios一直失败,可能是远程的后端数据的服务器倒闭了

如果想实现可以在本地写一个json,但是我做了四个小时的实验实在撑不下去了

所以就这样吧

放一下代码: 

javascript">//channelStore
import { createSlice } from '@reduxjs/toolkit'
//导入axiosimport axios from 'axios';
const channelStore= createSlice({name: 'channel',initialState: {channelList:[]},reducers: {setChannels(state, action) {state.channelList=action.payload}}
})
//异步请求部分
const {setChannels}=channelStore.actions
const fetchChannelList = () => {return async(dispatch) => {const res = await axios.get('https://geek.itheima.net/v1_0/channels').then(response => console.log(response.data)).catch(error => console.error("请求失败", error));dispatch(setChannels(res.data.data.channels))}
}
export { fetchChannelList }
const channelReducer = channelStore.reducer
export default channelReducer 
javascript">//\src\store\index.js
import { configureStore } from "@reduxjs/toolkit";
//导入子模块reducer
import counterReducer from './modules/counterStore';
import channelReducer  from './modules/counterStore';
const store= configureStore({reducer: {counter:counterReducer,channel:channelReducer
}})
export default store
javascript">//\src\App.jsx
import { useDispatch, useSelector } from "react-redux";
import { increment, decrement, addToNum } from './store/modules/counterStore'
import { fetchChannelList } from "./store/modules/channelStore";
import { useEffect } from 'react'
import { createSelector } from 'reselect'// 使用 createSelector 创建 memoized 选择器
const selectCount = createSelector(state => state.counter,counter => counter.count
);const selectChannelList = createSelector(state => state.channels, channels => channels?.channelList || [] // 确保不为空
);function App() {const count = useSelector(selectCount);const channelList = useSelector(selectChannelList);const dispatch = useDispatch();useEffect(() => {dispatch(fetchChannelList()); // 需要执行 fetchChannelList()}, [dispatch]);
//useEffect在App首次渲染的时候执行一次
//dispatch是ReduxStore里提供的方法,可以触发action,触发状态的更新return (<div className='App'><button onClick={() => dispatch(decrement())}>-</button><button onClick={() => dispatch(addToNum(0))}>点我置为0</button>{count}<button onClick={() => dispatch(increment())}>+</button><ul>{channelList.map(item => <li key={item.id}>{item.name}</li>)}//主要修改了这部分,把数据渲染出来</ul></div>);
}export default App;

在上面代码里最让人疑惑的是为什么dispatch会作为useEffect的依赖项,它是严格会改变的方法吗?

方法是函数,函数也可以做useEffect的依赖项,而react官方文档推荐useEffect里用到的所有外部变量都应该被作为依赖,确保useEffect能在变量变化时重新执行,如果 Redux 未来更新,导致dispatch变了,就可能导致useEffect访问的是过时的dispatch,可能引发 Bug

像这样,会报警告👆

Redux调试-devtools

点击Chart可以看到这样一个图,我是因为没获取到后端数据,所以节点很少

正常来说会给你一个这样的状态管理图

Raw可以看展示的数据

点击这个的左右按键可以回到上一次或下次状态改变的情况,方便做一些大项目的数据调试

还有一些redux的其他重要方法,后面会学到,例如store.subscribe(listener)、store.getState()


http://www.ppmy.cn/news/1577091.html

相关文章

数据结构篇—队列(queue)

一、引入 Tips&#xff1a;队列本应该跟栈一块讲的&#xff0c;奈何实在是学不完了&#xff0c;悄悄偷个懒&#xff0c;把这个拆成两部分来写QAQ。 紧接上文.................... 让我们把目光转到一条传送带上&#xff0c;让我们仔细观察一下这个物品在传送带上是怎么运行的呢…

试过了,多模态大模型Qwen/Qwen2.5-VL-3B-Instruct需要21G显存,我还是太天真啊!

前缘概述 之前说道,我想通过自己的笔记本(6G显存)部署一个Qwen/Qwen2.5-VL-3B-Instruct,最后因为显存不够,就放弃了。 Centos7,T4,几多磨难 但随后,我便开始了在一台系统为centos7,显卡为T4的机器上进行部署。总之就是很磨难,很多坑,最后还没有成功。 我猜测,相…

算法-数据结构-动态规划(有向图,到达一个节点的路径数量)

力扣题目&#xff1a;LCP 07. 传递信息 - 力扣&#xff08;LeetCode&#xff09; 小朋友 A 在和 ta 的小伙伴们玩传信息游戏&#xff0c;游戏规则如下&#xff1a; 有 n 名玩家&#xff0c;所有玩家编号分别为 0 &#xff5e; n-1&#xff0c;其中小朋友 A 的编号为 0每个玩家…

华为OD机试-最长的密码(Java 2024 E卷 100分)

题目描述 小王正在进行游戏大闯关,有一个关卡需要输入一个密码才能通过。密码获得的条件如下: 在一个密码本中,每一页都有一个由26个小写字母组成的密码,每一页的密码不同。需要从这个密码本中寻找这样一个最长的密码,从它的末尾开始依次去掉一位得到的新密码也在密码本…

MYOJ_1102:(洛谷P1540)[NOIP 2010 提高组]机器翻译

题目描述 小晨的电脑上安装了一个机器翻译软件&#xff0c;他经常用这个软件来翻译英语文章。 这个翻译软件的原理很简单&#xff0c;它只是从头到尾&#xff0c;依次将每个英文单词用对应的中文含义来替换。对于每个英文单词&#xff0c;软件会先在内存中查找这个单词的中文含…

Windows 图形显示驱动开发-WDDM 3.2-本机 GPU 围栏对象(二)

GPU 和 CPU 之间的同步 CPU 必须执行 MonitoredValue 的更新&#xff0c;并读取 CurrentValue&#xff0c;以确保不会丢失正在进行的信号中断通知。 当向系统中添加新的 CPU 等待程序时&#xff0c;或者如果现有的 CPU 等待程序失效时&#xff0c;OS 必须修改受监视的值。OS …

美股回测:历史高频分钟数据的分享下载与策略解析20250305

美股回测&#xff1a;历史高频分钟数据的分享下载与策略解析20250305 在金融分析和投资决策的精细化过程中&#xff0c;美股历史分钟高频数据发挥着至关重要的作用。这些数据以其详尽性和精确性&#xff0c;记录了股票每分钟的价格波动和成交量变化&#xff0c;为投资者提供了…

复试准备日常

实验室目前投了 aiot 这周四 感知计算面试3.5号下午2点开始&#xff08;面完了他问我有没有项目&#xff09; 532图像处理实验室&#xff08;我的项目大多也是图像处理的&#xff09;&#xff08;预计下周末&#xff09;提前到3.4号下午6点 我不在第一批里面 软专不知道要几个 …