React笔记(八)Redux

2023-11-04

一、安装和配置

React 官方并没有提供对应的状态机插件,因此,我们需要下载第三方的状态机插件 —— Redux。

1、下载Redux

在终端中定位到项目根目录,然后执行以下命令下载 Redux

npm i redux

2、创建配置文件

在 React 中,不会自动生成状态机的相关配置代码,因此,需要我们自己手动去创建目录以及配置文件。

我们可以在 src 的目录中创建一个 reduxstore 的目录,用来存放所有关于状态机的配置文件。然后,在该目录中创建一个 store.js 文件,作为整个状态机的入口文件。

3、配置状态机

3.1、创建store仓库对象
import {legacy_createStore as createStore} from 'redux'
const store=createStore();
3.2、保存数据到store中

createStore 方法接收一个函数作为参数,该函数的返回值,会保存到 store 仓库中

const store = createStore((state = 数据初始值) => {
    return state;
});

通过函数参数的默认值的方式,来设置仓库数据的初始值

3.3、查看仓库数据

store 仓库对象身上,提供了一个 getState() 方法,用来查看仓库中的所有数据:

console.log(store.getState());

由于目前没有任何文件中在引入状态机的配置文件,如果我们需要查看仓库中的数据,暂时需要在 index.js 中引入 /redux/store.js 文件来让其运行。

二、Redux核心概念

1、Redux工作流程

2、Redux组成部分

2.1、state

state:状态,就是我们传递的数据

2.2、action

action是一个通知对象,里面必须有一个type属性,表示当前通知的类型,至于其他属性,你可以任意添加

可以通过store.dispatch(action对象)来更新仓库中的数据

注意:

  • 在实际开发中,更多人喜欢用action创建函数

  • 在实际开发中,大多数情况下,type会被定义成字符串常量

2.3、reducer

reducer本质是一个函数,它用来响应发送过来的actions,经过处理把state发送给store

  • 在reducer函数中,需要return返回值,这样store才能接收到数据

  • reducer函数接收两个参数,第一个参数是初始化store,第二个参数是action

2.4、store

数据仓库,存放组件数据的地方。一个项目一般只有一个数据仓库,store可以把action和reducer联系在一起。

主要的职责

  • 维护应用的state

  • 提供getState()方法获取state

  • 提供dispatch()方法发送action

  • 通过subscribe()来注册监听

  • 通过subscribe()返回值来注销监听

三、第一个Redux程序

1、创建action

  • 在src目录下创建一个actions文件夹

  • 在该目录下创建一个index.js文件

  • action是一个通知对象,里面必须有一个type属性,这里的num属性是自定义的表示计数器每次增加的个数

const incrementAction={
    type:'increment',
    num:1
}
export {incrementAction}

2、创建reducer

  • 在src目录下创建reducers目录

  • 在该目录下创建index.js文件,用来构建reducer,注意reducer要接收两个参数

  • 第一个参数是默认状态,我们可以定义一个初始化的state,然后进行赋值

  • 在函数里面判断第二个参数action的type值是否是我们发送过的,如果是,我们可以通过return返回新的state

const counterReducer=(state=0,action)=>{
   switch(action.type){
        case 'increment':
            return state+action.num
        default:
            return state
    }
}
export {counterReducer}

3、创建store

  • 在src目录下创建一个文件夹store

  • 在该目录下创建index.js文件,用来构建store,注意createStore函数第一个参数接收的是reducer

import {legacy_createStore as createStore} from 'redux'
import {counterReducer} from '../reducers'
const store=createStore(counterReducer)
export default store

4、在组件中使用

  • 创建components文件夹,该目录下存放自定义组件

  • 在该目录下新建Counter.jsx文件

  • 将Counter.jsx引入到App.js文件中

import Counter from "./components/Counter";
function App() {
  return (
    <div>
        <Counter></Counter>
    </div>
  );
}
export default App;
  • 调用dispatch函数,更新仓库的数据

import React,{useEffect,useState} from 'react'
import store from '../redux/store'
import {incrementAction} from '../redux/actions'

export default function Counter() {
  const increment=()=>{
      store.dispatch(incrementAction)
  }
  return (
    <div>
        <h1>计数器:{store.getState()}</h1>
        <button onClick={()=>{increment()}}>+</button>
    </div>
  )
}
  • 当组件加载完毕后,调用 store. subscribe注册监听,监听state数据的变化

import React,{useEffect,useState} from 'react'
import store from '../redux/store'
import {incrementAction} from '../redux/actions'

export default function Counter() {
  const [count, setCount] = useState(0);
  const increment=()=>{
      store.dispatch(incrementAction)
  }
  useEffect(()=>{
    store.subscribe(()=>{

        console.log('正在监控'+store.getState());
        setCount({})
    })
  })

  return (
    <div>
        <h1>计数器:{store.getState()}</h1>
        <button onClick={()=>{increment()}}>+</button>
    </div>
  )
}

四、redux中action和reducer的优化

1、action优化

1.1、action creator

我们一般会在页面里面修改数据,即,在页面里面调用store的dispatch方法。那就意味着action对象中的num字段很大可能是动态的,即不同的页面num字段可能是不同的,这样我们就不能把action写成一个死的对象,最好是封装成一个函数,执行过后返回一个action通知对象,然后num的值通过函数的参数来决定。这样的函数我们称之为action创建函数(action creator)

const incrementAction=(num)=>{
    return{
        type:'increment',
        num:num
    }
}
export {incrementAction}
1.2、type常量

在实际开发中,type在多数情况下会被定义成常量,如下所示

  • 在src/actions目录下,新建actionTypes.jsx文件,将所有type类型定义成常量

export const INCREMENT ="increment"
  • 在action creator中引入

import {INCREMENT} from './constant'
const incrementAction=(num)=>{
    return{
        type:INCREMENT,
        num:num
    }
}
export {incrementAction}

2、reducer优化

项目中肯定不止一个数据,所以state的默认值应该是一个对象,而不是其他。而且除了默认值,每当case到一个条件,返回一个新的对象时,应该返回一个全新的对象,然后才是你要修改的数据(当然这些数据,如果是引用类型的话,应该修改其引用本身,否则界面可能不会更新,尽管数据发生变化了

import { INCREMENT } from '../actions/constant'
const counterReducer = (state = { num: 0 }, action) => {
    switch (action.type) {
        case INCREMENT:
            return {
                ...state,
                num: state.num + action.num
            }
        default:
            return state
    }
}
export { counterReducer }

优化后,在组件中调用的代码如下

import React,{useEffect,useState} from 'react'
import store from '../redux/store'
import {incrementAction} from '../redux/actions'

export default function Counter() {
  const [count, setCount] = useState(0);
  const increment=()=>{
      store.dispatch(incrementAction(2))
  }
  useEffect(()=>{
    store.subscribe(()=>{

        console.log('正在监控'+store.getState());
        setCount({})
    })
  })

  return (
    <div>
        <h1>计数器:{store.getState()}</h1>
        <button onClick={()=>{increment()}}>+</button>
    </div>
  )
}

五、react-redux概述

为了方便使用,Redux 的作者封装了一个 React 专用的库 React-Redux,本文主要介绍它。

React-Redux 将所有组件分成两大类:UI 组件(presentational component)和容器组件(container component)。

1、UI组件

UI 组件有以下几个特征。

  • 只负责 UI 的呈现,不带有任何业务逻辑

  • 没有状态(即不使用this.state这个变量)

  • 所有数据都由参数(this.props)提供

  • 不使用任何 Redux 的 API

2、容器组件

容器组件有以下几个特征。

  • 负责管理数据和业务逻辑,不负责 UI 的呈现

  • 带有内部状态

  • 使用 Redux 的 API

总之,只要记住一句话就可以了:UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑。

React-Redux 规定,所有的 UI 组件都由用户提供,容器组件则是由 React-Redux 自动生成。也就是说,用户负责视觉层,状态管理则是全部交给它。

六、react-redux基本使用

1、安装react-redux

npm i react-redux
npm i redux

2、创建action

  • 在constant.jsx中添加INCREMENT

export const INCREMENT="increment";
  • 在src/actions/index.jsx中添加incrementAction

import { INCREMENT } from "./constant";
export const incrementAction=num=>({type:INCREMENT,num})

3、创建reducer

  • 在src/reducers/index.jsx中添加counterReducer

import {INCREMENT} from '../actions/constant'
const counterReducer=(state={num:0},action)=>{
    switch(action.type){
        case INCREMENT:
            return{
                ...state,
                num:state.num+action.num
            }
        default:
            return state
    }
}
export {counterReducer}

4、创建store

在src/store/index.jsx中编写代码如下

import {legacy_createStore as createStore} from 'redux'
import {counterReducer} from '../reducers'
const store=createStore(counterReducer)
export default store

5、全局注入store仓库

  • 在index.js中导入Provider组件

import {Provider} from 'react-redux'
  • 利用provider组件将整个接口进行包裹

  • 给Provider组件设置store的属性,该属性的值就是通过createStore构建出来的store实例对象

import ReactDOM from 'react-dom/client'
import App from './App';
import {Provider} from 'react-redux'
import store from './store'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <Provider store={store}>
      <App />
    </Provider>
);
  • Provider在根组件外面包了一层,这样一来,App的所有子组件就默认都可以拿到state了。

6、组件关联仓库

由于UI组件不能使用Redux的API所以,如果在组件中如果要使用Redux就必须将UI组件变成容器类组件

React-Redux 提供connect方法,用于从 UI 组件生成容器组件。connect的意思,就是将这两种组件连起来

import React from 'react'
import { connect } from 'react-redux'

function Counter() {
  render() {
    return (
      <div>
          
      </div>
    )
  }
}
export default connect()(Counter)

7、组件操作仓库

7.1、获取仓库数据

connect 方法接收一个回调函数作为参数:

const mapStateToProps = () => {

}
export default connect(mapStateToProps)(Counter);

该回调函数本身,又可以通过第一个参数接收到仓库中的所有数据

const mapStateToProps = (state) => {
    console.log(state); // 仓库中所有的数据
}
export default connect(mapStateToProps)(Counter);

在该回调函数中,返回一个对象,该对象会和组件的 props 进行合并。换句话说,该函数的返回值会添加到组件的 props 中:

const mapStateToProps = (state) => {
    return {
        数据名: 从 state 中获取的数据值
    }
}
export default connect(mapStateToProps)(Counter);

处理完成后,我们就可以在组件的 props 中访问到对应的仓库数据了:

function Counter(props)
    render() {
        return (
            <div>
                <h1>计数器</h1>
                <h2>{props.数据名}</h2>
            </div>
        )
    }
}
7.2、修改仓库数据

修改仓库数据,依然是通过 dispatch() 方法来触发修改操作。

在组件中,只要和仓库关联过,就能在 props 上直接获取到 dispatch 方法。因此,在组件中可以直接通过 props.dispatch() 方法来触发修改数据的操作。

import React,{useEffect,useState} from 'react'
import store from '../redux/store'
import {incrementAction} from '../redux/actions'
import { connect } from 'react-redux';

function Counter(props) {
  const increment=()=>{
      props.dispatch(incrementAction(3))
  }
  return (
    <div>
        <h1>计数器:{props.num}</h1>
        <button onClick={()=>{increment()}}>+</button>
    </div>
  )
}
const mapStateToProps = (state) => {
    console.log(state); // 仓库中所有的数据
    return{
       num:state.num
    }
}
export default connect(mapStateToProps)(Counter)

七、状态机的Hook

针对 React 中的函数组件,React-Redux 中也提供了第三方的 Hook。

1、useSelector

通过调用 useSelector 方法,并传递一个回调函数作为参数,我们可以在这个回调函数中获取到仓库中的 state。

import { useSelector } from 'react-redux'
useSelector((state) => {
    console.log(state);  // 仓库中所有的数据
})

然后我们可以在回调函数中,将我们需要使用的数据 return 出来,然后用一个变量来接收:

const data = useSelector((state) => {
    return 数据;
})

后续组件要使用数据,就可以直接通过变量进行数据的访问了。

2、useDispatch

调用 useDispatch() 方法,可以直接获取到 dispatch() 方法。

import { useDispatch } from 'react-redux'
export default fucntion Test() {
    const dispatch = useDispatch();
    return (
    )
}

如果组件中使用 Hook 来获取 dispatch 方法的话,就不再需要使用 connect 来对组件和仓库进行关联了。

获取到 dispatch 方法后,后续的使用就和 props.dispatch 的使用一致。

关键代码

import React from 'react'
import {useDispatch,useSelector} from 'react-redux'
import {incrementAction} from '../../redux/action'


export default function Counter(props) {
  const num=useSelector((state)=>{
    return state.num
  })
  const dispatch=useDispatch()
  const increment=()=>{
    dispatch(incrementAction(3))
  }
  return (
    <div>
        <h1>计数器:{num}</h1>
        <button onClick={()=>{increment()}}>+</button>
    </div>
  )
}

八、reducer拆分

当项目越来越大的时候,需要管理的数据也会越来越多,如果所有的数据都由一个reducer管理的话,则这个reducer肯定会变得非常的臃肿,且难以维护。所以有必要对reducer做一个拆分,不同功能模块的数据切片,由不同的reducer来管理。假设现在有两个模块,账户管理模块和商品管理模块,每个模块都有数据需要管理

import {combineReducers} from 'redux'
import counterReducer from './counterReducer'
export default combineReducers({
    counter:counterReducer
})

注意:调用时候需要使用使用到切片的名字才能访问到,比如

import React from 'react'
import {useSelector} from 'react-redux'
export default function Counter(props) {
  const count=useSelector((state)=>{
    console.log(state);
    return state.counter.num      //state.切片名的key.num
  })
  return (
    <div>
        <h1>计数器:{count}</h1>
    </div>
  )
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

React笔记(八)Redux 的相关文章

随机推荐

  • 面试官:请写一个你认为比较“完美”的单例

    单例模式是保证一个类的实例有且只有一个 在需要控制资源 如数据库连接池 或资源共享 如有状态的工具类 的场景中比较适用 如果让我们写一个单例实现 估计绝大部分人都觉得自己没问题 但如果需要实现一个比较完美的单例 可能并没有你想象中简单 本文
  • CNCC2020丨5G边缘智能与智慧城市论坛

    CNCC 技术论坛 5G 边缘智能与智慧城市 探索 5G 时代下边缘智能与智慧城市的融合 时间 10 月 23 日下午 16 00 18 30 主办方 天津大学 国防科技大学 模式 线上 线下 线上地点 https cncc ccf org
  • C++11常用新特性

    1 constexpr 使修饰的函数在编译期执行 constexpr int numpow int x int y while y x x return x int a numpow 2 4 数组的大小必须是常量 但我们用constpr修饰
  • [1016]DataFrame一列拆成多列以及一行拆成多行

    文章目录 DataFrame一列拆成多列 DataFrame一行拆成多行 分割需求 简要流程 详细说明 0 初始数据 1 使用split拆分 2 使用stack行转列 3 重置索引 删除多余的索引 并命名为C 4 使用join合并数据 Da
  • 【滤波跟踪】基于无迹卡尔曼滤波实现惯性导航+DVL的组合导航算法附matlab代码

    1 内容介绍 水下航行器 的产生 发展直至实际使用经过了很长的历程 对于海洋人们从了解认识 开发研究 使用和保护等方面的发展 促使着水下航行器的开发研究工作也逐渐活跃起来 在新世纪中水下航行器还将被重点开发应用 水下航行器研制的重要标准之一
  • EXT4文件系统挂载成功后执行init文件linuxrc失败

    Hi3559av100平台 制作EXT4文件系统在EMMC存储设备上启动 发现EXT4文件系统挂载成功后执行init文件linuxrc失败 分析原因如下 错误打印 sdhci Secure Digital Host Controller I
  • 王道考研——计算机网络(第一章 计算机网络体系结构)

    1 0认识计算机网络 在下载电影 不会出现乱序问题 和微信收发消息 比如表情包乱序了 所使用的协议是不同的 1 1 1概念和功能 1 计算机网络的概念 2 计算机网络的功能 3 计算机网络的发展 第一阶段 小写的 internet 就是这样
  • 第七课 学习韦东山老师STM32F103 迷你核心单片机Mini开发板

    本节课为 7天物联网智能家居实战训练营1期 的第7节课 也是最后1节课 文章目录 一 基础班7节课 项目完善与总结回顾 二 项目实例优化 三 总结回顾 四 互动环节 一 基础班7节课 项目完善与总结回顾 第一部分 一 对项目进行功能改进优化
  • 应用架构COLA 2.0

    很多同学不止一次和我反馈 我们的系统很混乱 主要表现在 应用的层次结构混乱 不知道应用应该如何分层 应该包含哪些组件 组件之间的关系是什么 缺少规范的指导和约束 新加一段业务逻辑不知道放在什么地方 哪个类 哪个包 应该起什么名字比较合适 解
  • Arduino篇(二)火焰、烟雾、CO等多个传感器组合实验设计

    最近用Arduino做了一个简单的多个传感器组合的实验 比较简单 设想的应用场景是火灾检测 如下图所示 一 实验材料 MQ 2烟雾传感器 5路火焰传感器 CO一氧化碳传感器 无源蜂鸣器 LED灯 arduino mega开发板 面包板以及杜
  • java面试汇总

    java面试汇总 基础篇 1 一个 java 源文件中是否可以包括多个类 不是内部类 有什么限制 可以有多个类 但只能有一个public的类 并且public的类名必须与文件名相一致 2 Java有没有goto java中的保留字 现在没有
  • 神经网络学习小记录-番外篇——常见问题汇总

    神经网络学习小记录 番外篇 常见问题汇总 前言 问题汇总 1 下载问题 a 代码下载 b 权值下载 c 数据集下载 2 环境配置问题 a 20系列所用的环境 b 30系列显卡环境配置 c CPU环境配置 d GPU利用问题与环境使用问题 e
  • springcloud配置中心_Spring Cloud集成Apollo分布式配置中心(含源代码)

    在Spring Cloud项目中使用Apollo 分布式配置中心之前 需要先启动Apollo的配置服务端 具体操作步骤 可参看如下链接 携程Apollo 阿波罗 分布式配置中心 总体架构设计和核心概念 携程Apollo 分布式配置中心 本地
  • 2021长安二中高考成绩查询,最全!2020年陕西省50所高中高考成绩汇总&数据分析!...

    原标题 最全 2020年陕西省50所高中高考成绩汇总 数据分析 目前 陕西省部分高中学校2020年的高考成绩流传出来 为家长们津津乐道 本文汇总了包含西安 宝鸡 咸阳 渭南 延安等各地市共50所学校的高考成绩 供家长们参考 2020年西安市
  • Spring启动的bean回调函数

    启动时调用 Spring启动时bean会按照如下顺序进行回调 构造函数 实现ApplicationContextAware的函数 PostConstruct标注的函数 实现InitializingBean接口的函数 Bean注解中initM
  • Boost练习程序(智能指针)

    include
  • 前端项目review之修改element-ui全局主题颜色配置element-theme-chalk和gulp

    每个公司的主题风格肯定是不一样的 比如现在的公司主题就是 00ab7a 在PC端TO B的项目中少不了用element ui 这个时候用element theme chalk直接本地编译修改了element全局的主题色 一 修改默认主题配色
  • 爬虫的概念

    爬虫 模拟浏览器的行为 向服务器发送请求 获取响应中的数据 往白了说就是模拟浏览器的行为 掌握要点 如何更像的模拟浏览器的行为 总结 浏览器能拿到的数据 就一定可以爬到 浏览器拿不到的数据 就一定爬不到
  • 【Ubuntu】Ubuntu系统启动过程中,输入用户名与密码后登录一直卡在紫色界面问题(未解决,最后通过重装系统)

    Ubuntu Ubuntu系统启动过程中 输入用户名与密码后登录一直卡在紫色界面问题 未解决 最后通过重装系统 参考文章 1 Ubuntu Ubuntu系统启动过程中 输入用户名与密码后登录一直卡在紫色界面问题 未解决 最后通过重装系统 2
  • React笔记(八)Redux

    一 安装和配置 React 官方并没有提供对应的状态机插件 因此 我们需要下载第三方的状态机插件 Redux 1 下载Redux 在终端中定位到项目根目录 然后执行以下命令下载 Redux npm i redux 2 创建配置文件 在 Re