当前位置: 首页 > article >正文

React:Redux

Redux引入

Redux感觉像组件通信的中介

state存放被管理的状态

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

reducer是怎么修改数据

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

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

自己实现redux对状态的管理

首先进行初始化:

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

createSlice({
  name: 'counter', // slice 的名称
  initialState: {
    count: 0, // 初始状态
  },
});

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

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

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

解构获取这两个方法:

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

获取reducer:

//获取reducer
const reducer = counterStore.reducer

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

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

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

然后在index.js文件里组合

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

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

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

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

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

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

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

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

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

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

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

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并调用就好了

一些注意事项

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

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

这叫命名导出/导入:

//命名导出
const name = 'John';
function greet() {
  console.log('Hello!');
}

export { name, greet }; // 导出多个内容
//命名导入
import { name, greet } from './module';

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

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

react组件里修改store的数据

引入App.js

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

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的值

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

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

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

异步状态
 

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

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

所以就这样吧

放一下代码: 

//channelStore
import { createSlice } from '@reduxjs/toolkit'
//导入axios
 import 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 
//\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
//\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.kler.cn/a/578730.html

相关文章:

  • Deeplabv3+改进1:添加CBAM注意力机制|有效涨点
  • 大道至简:道法自然的应用秘诀
  • Python实例:PyMuPDF实现PDF翻译,英文翻译为中文,并按段落创建中文PDF
  • 整理一下arcGis desktop版本软件, 从入门到精通需要学习的知识点
  • 苦瓜书盘官网,免费pdf/mobi电子书下载网站
  • PawSQL for MSSQL:PawSQL 支持 SQL Server 的SQL优化、SQL审核、性能巡检
  • Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
  • LeetCode1275
  • MySQL入门手册
  • Redis-限流方案
  • 《Python实战进阶》No18: 使用 Apache Spark 进行分布式计算
  • 轻量级TCC框架的实现
  • ZerotTier -- 开源、不限流、实现远程连接的内网穿透工具(window环境)
  • std::vector的模拟实现
  • Python----数据可视化(Seaborn二:绘图一)
  • vue管理系统常规布局思路,头部+菜单+主题(Container 布局容器)
  • 【编译器】VSCODE搭建ESP32-C3
  • C++【类和对象】
  • 第四届大数据、区块链与经济管理国际学术会议
  • Spring使用@Scheduled注解的参数详解