React学习05 - redux

文章目录

  • redux工作流程
    • redux理解
    • redux理解及三个核心概念
    • redux核心api
    • redux异步编程
    • react-redux
      • 组件间数据共享
    • 纯函数
    • redux调试工具
    • 项目打包

redux工作流程

redux理解

redux是一个专门用于状态管理的JS库,可以用在react, angular, vue 等项目中。在与react配合使用时,集中式管理react应用中多个组件共享的状态。
当某个组件的状态需要让其他组件随时可以拿到时,当一个组件需要改变另一个组件的状态时,可以使用redux。
为什么不用消息订阅与发布?消息订阅与发布需要组件挂载后才能发布获取消息,当想要不挂载组件,先发布消息,再挂载组件接收消息的时候,就需要redux。
在react中使用redux,需要使用Redux Toolkit(RTK)和react-redux。

redux理解及三个核心概念

redux原理图

根据原理图,例如,当页面需要将一个数据+2,当前该数据为0 。

  1. Action Creators会创建一个action,把这个action分发(dispatch)给store,我们给action执行的操作自命名为Add,操作的数据为2。
  2. store接收到action后,将当前的状态0,和action一起交给Reducers,Reducers在原状态0的基础上,执行操作Add,也就是+2,然后返回新状态数据2 。
  3. Store接收到新状态,页面组件可以通过getState方法获取新状态数据。

当页面初次加载时,是在Reducers内初始化状态的,原状态为undefined,操作为初始化,Reducers可以初始化和加工状态。
三个核心概念:action, reducer, store。
action 包含两个属性,1.type 标识性属性,唯一且必要,值为字符串,2.data 数据属性,可选,值为任意类型。每个组件都可以有自己的ActionCreator。

{type:'自定义操作名称', data:数据} 

reducer是一个函数,根据旧的state和action产生新的state。每个组件都可以有自己的reducer。
store是一个对象,将state, action, reducer联系在一起。

import {createStore} from 'redux'
import reducer from './reducers'
const store = createStore(reducer)  // 获取store对象

通常集中状态管理的部分会在src下单独创建一个store目录,当建立很多子store模块时,在store目录下创建一个modules文件夹,包含所有子store模块文件,在store文件夹的index.js中将所有子模块组合并导出store。

redux核心api

通过案例代码理解store三个核心API: getState, dispatch, subscribe。在src下新建目录redux,创建store.js和demo_reducer.js两个文件,终端增加redux包。

npm add redux

redux4.0.5

// store.js
// 引入创建store的方法
import {createStore} from 'redux'
// 引入为组件服务的reducer
import demoReducer from './demo_reducer'
// 创建并暴露store
const store = createStore(demoReducer)
export default store

// demo_reducer.js  为Demo组件服务的reducer,本质是函数,返回新状态
const initState = 0; // 初始化状态
export default function demoReducer(preState=initState,action){
  const {type,data} = action;
  switch(key){
    case 'increment':
      return preState + data;
    case 'decrement':
      return preState - data;
    default:
      return 0; //无指定操作,即为初始化状态操作
  }
}

// Demo组件
import store from '../../redux/store'
export default class Demo extends Component{
  componentDisMount(){
    // 检测redux的状态更新,当前页面的状态随redux中的更新
    store.subscribe(()=>{
      this.setState({})
    })
  }
  add = ()=>{
    store.dispatch({type:'increment',data:2})
  }
  render(){
    return (
      <div>
        <h1>当前值:{store.getState()}</h1>
        <button onClick={this.add}>2</button>
      </div>
    )
  }
}

// App index.js Demo组件的subscribe可以去掉,放到App里,避免多个组件需要订阅重复代码
store.subscribe(()=>{
  ReactDOM.render(<App/>,document.getElementById('root'))
})

以上就完成了一个精简的redux。下面增加ActionCreator,将redux完善。在redux目录下创建demo_action.js。对应修改组件里调用dispatch。

// demo_action.js
export default function createIncrementAction(data){
  return {type:'increment',data}
}

// Demo组件
// 引入actionCreator,专门用于创建action对象
import createIncrementAction from '../../redux/demo_action'
// add方法改为
add = ()=>{
  store.dispatch(createIncrementAction(2))
}

然后在redux目录下新建constant.js,用来将操作名集中管理,避免写错字符串。对应的修改reducer和action里的操作名。

// constant.js
export const INCREMENT = 'increment';
export const DECREMENT = 'decrement';

// demo_reducer.js
import {INCREMENT,DECREMENT} from './constant.js'
switch(key){
    case INCREMENT:
      return preState + data;
    case DECREMENT:
      return preState - data;
    default:
      return 0; 
  }

// demo_action.js
import {INCREMENT,DECREMENT} from './constant.js'
export default function createIncrementAction(data){
  return {type:INCREMENT,data}
}

以上就是完整的redux,有action,store,reducer,组件和constant五个文件内容,其中组件、store、reducer是必须的。

redux异步编程

action一般为一个对象,action也可以为一个函数,当它为对象时,是同步的,当它是函数时,是异步的。异步action应该包含ajax,计时器等异步任务,对象和数组满足不了,所以异步的action要使用函数实现。

// Demo组件
import store from '../../redux/store'
export default class Demo extends Component{
  componentDisMount(){
    // 检测redux的状态更新,当前页面的状态随redux中的更新
    store.subscribe(()=>{
      this.setState({})
    })
  }
  asyncAdd = ()=>{
    setTimeout(()=>{
      store.dispatch(createIncrementAction(2))
    },500) // 等待500毫秒后再执行加2,等待动作在组件里,组件等待500毫秒后发送请求去让redux执行一系列操作
  }
  render(){
    return (
      <div>
        <h1>当前值:{store.getState()}</h1>
        <button onClick={this.asyncAdd}>异步加2</button>
      </div>
    )
  }
}

// demo_action.js
import {INCREMENT,DECREMENT} from './constant.js'
export default function createIncrementAction(data){
  return {type:INCREMENT,data}
}

以下尝试实现一个函数类型的action,理想store接收到函数类型的action,丢给reducer处理,等待500毫秒后再通过store.dispatch去生成一个对象类型的action,然后reducer执行+2操作。实际会报错,action传递到store处理必须要是对象类型的。

// 修改组件内异步加2的方法
  asyncAdd = ()=>{
      store.dispatch(createAsyncIncrementAction(2,500)); //组件立即发送请求,让redux去等待500毫秒后再执行一系列操作
  }

// 在action文件中增加createAsyncIncrementAction方法,实现异步action
export const createAsyncIncrementAction = (data,time)=>{
  return ()=>{
    setTimout(()=>{
      store.dispatch(createIncrementAction(data))
    },time)
  } // return的是一个函数类型的action,里面包含异步任务。store接收到函数类型的action,丢给reducer处理不了函数类型的,所以返回报错
}

引入中间件,当返回的action是一个函数包含异步任务时,由中间件处理异步部分,将异步任务完成后生成的对象类型的action传递给store,再去真正操作数据,解决报错。

npm add redux-thunk
// store.js
// 引入创建store的方法
import {createStore,applyMiddleware} from 'redux'
// 引入为组件服务的reducer
import demoReducer from './demo_reducer'
// 引入redux-thunk
import thunk from 'redux-thunk'
// 创建并暴露store
const store = createStore(demoReducer,applyMiddleware(thunk))
export default store

// 在action文件中增加createAsyncIncrementAction方法,实现异步action
export const createAsyncIncrementAction = (data,time)=>{
  return ()=>{
    setTimout((dispatch)=>{
      dispatch(createIncrementAction(data))
    },time)
  } // return的是一个函数类型的action,里面包含异步任务。store接收到函数类型的action,传递给中间件处理,此时在处理的依旧是store,所以可以传递dispatch函数,免去store.dispatch调用
}

react-redux

react-redux用来连接redux和react组件。
所有的UI组件都应该包含一个在一个容器组件里,是父子关系。
容器组件才能使用redux的API与redux打交道,UI组件不能使用任何redux的API。
容器组件会通过props传给UI组件redux中保存的状态和用于操作状态的方法。
react-redux模型图
UI组件内不能含有redux,创建容器组件,在容器组件内引入react-redux

npm add react-redux

根据模型图,容器组件要向外连接redux,向内连接UI组件。容器组件通过react-redux连接UI组件,通过store连接redux。

// 容器组件DemoContainer/index.jsx
// 1.引入UI组件
import DemoUI from '../../components/Demo'
// 2.引入react-redux, connect用于连接UI组件和redux
import {connect} from 'react-redux'
// 3.连接UI组件和容器组件
const DemoContainer = connect()(DemoUI); // connect是一个函数,返回值依然是一个函数
export default DemoContainer; // 向外暴露一个容器组件

// 在App内
// 1.引入容器组件,不引入UI组件
import DemoContainer from './containers/DemoContainer'
// 2.引入store
import store from '../../redux/store'
export default class App extends Component{
  render(){
    return (
      <div>
        <DemoContainer store={store}/>
      </div>
    ) // 3.连接容器组件和redux,给容器组件传递store
  }
}

容器组件向UI组件通过props传值,通过connect函数的第一个参数,connect第一个参数为函数,返回的值作为UI组件的状态。connect第二个参数为函数,返回值作为UI组件操作状态的方法。

// mapStateToProps用于传递状态,返回一个对象,key和value对应props的key value
function mapStateToProps(state){
  // return {n:900}; // 相当于<DemoUI n={900} />
  return {n:state}
}
// mapDispatchToProps用于传递操作状态的方法,返回一个对象,key和value对应props的key value
function mapDispatchToProps(dispatch){
  return {add: (data)=>{
   dispatch(createIncrementAction(data)) // 通知redux做加法
  }} 
}
export default connect(mapStateToProps,mapDispatchToProps)(DemoUI) 

容器组件的store是靠props传进去的,而不是引入的。

mapDispatchToProps简化

// ui组件
increment = ()=>{
  const value = this.selectNumber;
  this.props.jia(value)
}

// 容器组件mapDispatchToProps
const mapDispatchToProps = dispatch=>({
  jia:number => dispatch(createIncrementAction(number)),
  jian: number => dispatch(createDecrementAction(number))
})
// 继续简化,将mapStateToProps和mapDispatchToProps省略,直接使用connect函数
export default connect(
 state => ({n:state}), //mapStateToProps简写
 {
   jia:createIncrementAction,
   jian:createDecrementAction
 } // mapDispatchToProps简写
)(DemoUI) 

容器组件使用connect连接ui组件和redux,容器组件可以自调用subscribe更新页面,所以可以省略store.subscribe的调用。
可以在App index.js内给所有子组件传递store,在子组件内可以不引入store直接使用

// App index.js
import store from './redux/store'
import {Provider} from 'reatc-redux'
ReactDOM.render(
  <Provider store={store}>
    <App/>
  </Provider>,
  document.getElementById('root')
)

UI组件可以写在容器组件的文件内,合并为一个文件,只暴露容器组件。
总结,组件与redux打交道:

  1. 定义UI组件,不暴露
  2. 使用connect生成容器组件,并暴露
    connect(
    state => ({key:value}), // 映射状态
    {key:xxxAction} // 映射操作状态的方法
    )(UI组件)
  3. 在UI组件内通过this.props.xxx读取和操作状态
// UI组件和容器组件合并
class Demo extends Component{
  add = ()=>{
    this.props.jiafa(1)
  }
  render(){
    return (
      <div>
        <h1>和为:{this.props.sum}</h1>
        <button onClick={this.add}>点击+1</button>
      </div>
    )
  }
}

export default connect(
  state => ({sum:state}), 
  {jiafa:createIncrementAction} 
)(Demo)

组件间数据共享

创建Person组件,创建对应的action, reducer,在store内使用combineReducers把所有组件的reducer合并。

// store.js
import demoRudecer from './reducers/demo'
import personReducer from './reducers/person'
import {combineReducers} from 'redux'
const allReducer = combineReducers({
  sum:demoReducer,
  persons:personReducer
}) // combineReducers里传递的对象就是store管理的状态对象

// Demo组件里connect使用
state => ({s: state.sum, num: state.persons.length}) // 传递给UI组件使用

纯函数

纯函数只要是同样的输入(实参),必定得到同样的输出(返回)。
纯函数内不能改写参数数据,不能产生副作用(比如不能发送网络请求、不能连接输入输出设备等有可能造成数据丢失的行为),不能调用Date.now或Math.random等返回值不定的方法。
redux的reducer函数必须是一个纯函数。

redux调试工具

引入工具库,可以在浏览器看到redux内的状态

npm add redux-devtools-extension
// store.js
// 引入工具库
import {composeWithDevTools} from 'redux-devtools-extention'
export default createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))

项目打包

执行命令生成build文件夹

npm run build

将生成的build文件夹放到服务器,使用Node或Java搭建服务器,或者使用serve库。

npm i serve -g // 全局安装库
serve a  // 以a文件夹为根目录作为服务器运行
serve // 以当前文件为服务器运行

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024年11月最新 Alfred 5 Powerpack (MACOS)下载

在现代数字化办公中&#xff0c;我们常常被繁杂的任务所包围&#xff0c;而时间的高效利用成为一项核心需求。Alfred 5 Powerpack 是一款专为 macOS 用户打造的高效工作流工具&#xff0c;以其强大的定制化功能和流畅的用户体验&#xff0c;成为众多效率爱好者的首选。 点击链…

batchnorm与layernorn的区别

1 原理 简单总结&#xff1a; batchnorn 和layernorm是在不同维度上对特征进行归一化处理。 batchnorm在batch这一维度上&#xff0c; 对一个batch内部所有样本&#xff0c; 在同一个特征通道上进行归一化。 举个例子&#xff0c; 假设输入的特征图尺寸为16x224x224x256&…

【c++丨STL】stack和queue的使用及模拟实现

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;C、STL 目录 前言 一、什么是容器适配器 二、stack的使用及模拟实现 1. stack的使用 empty size top push和pop swap 2. stack的模拟实现 三、queue的…

ApiChain 从迭代到项目 接口调试到文档生成单元测试一体化工具

项目地址&#xff1a;ApiChain 项目主页 ApiChain 简介 ApiChain 是一款类似 PostMan 的接口网络请求与文档生成软件&#xff0c;与 PostMan 不同的是&#xff0c;它基于 项目和迭代两个视角管理我们的接口文档&#xff0c;前端和测试更关注版本迭代中发生变更的接口编写代码…

力扣面试题 - 24 插入

题目&#xff1a; 给定两个整型数字 N 与 M&#xff0c;以及表示比特位置的 i 与 j&#xff08;i < j&#xff0c;且从 0 位开始计算&#xff09;。 编写一种方法&#xff0c;使 M 对应的二进制数字插入 N 对应的二进制数字的第 i ~ j 位区域&#xff0c;不足之处用 0 补齐…

网络安全,文明上网(4)掌握网络安全技术

前言 在数字化时代&#xff0c;个人信息和企业数据的安全变得尤为重要。为了有效保护这些宝贵资产&#xff0c;掌握一系列网络安全技术是关键。 核心技术及实施方式 1. 网络监控与过滤系统&#xff1a; 这些系统构成了网络防御体系的基石&#xff0c;它们负责监控网络通信&…

[开源] SafeLine 好用的Web 应用防火墙(WAF)

SafeLine&#xff0c;中文名 “雷池”&#xff0c;是一款简单好用, 效果突出的 Web 应用防火墙(WAF)&#xff0c;可以保护 Web 服务不受黑客攻击 一、简介 雷池通过过滤和监控 Web 应用与互联网之间的 HTTP 流量来保护 Web 服务。可以保护 Web 服务免受 SQL 注入、XSS、 代码注…

ELK8.15.4搭建开启安全认证

安装 Elastic &#xff1a;Elasticsearch&#xff0c;Kibana&#xff0c;Logstash 另外安装一个收集器filebeat 通过二进制安装包进行安装 创建一个专门放elk目录 mkdir /elk/ mkdir /elk/soft下载 es 、kibana、Logstash、filebeat二进制包 cd /elk/softwget https://art…

excel版数独游戏(已完成)

前段时间一个朋友帮那小孩解数独游戏&#xff0c;让我帮解&#xff0c;我看他用电子表格做&#xff0c;只能显示&#xff0c;不能显示重复&#xff0c;也没有协助解题功能&#xff0c;于是我说帮你做个电子表格版的“解题助手”吧&#xff0c;不能直接解题&#xff0c;但该有的…

Linux上安装单机版Kafka

1、上传Kafka安装包至Linux并进行解压 kafka_2.12-1.1.1.tgz 链接&#xff1a;https://pan.baidu.com/s/1i41RKHlCbp0q2xQ1PEgT5g 提取码&#xff1a;vofa 将安装包解压 tar -zxvf kafka_2.12-1.1.1.tgz 2、修改kafka配置 vi config/server.properties 只修改绑定 IP &#…

Kafka 消费者全面解析:原理、消费者 API 与Offset 位移

Kafka&#xff1a;分布式消息系统的核心原理与安装部署-CSDN博客 自定义 Kafka 脚本 kf-use.sh 的解析与功能与应用示例-CSDN博客 Kafka 生产者全面解析&#xff1a;从基础原理到高级实践-CSDN博客 Kafka 生产者优化与数据处理经验-CSDN博客 Kafka 工作流程解析&#xff1a…

AUTOSAR - 接口

Application Port Interface&#xff0c;Service Port Interface&#xff0c;除了IS-SERVICE字段外&#xff0c;其余都相同。 ClientServer 支持IsService <CLIENT-SERVER-INTERFACE UUID"523b6eb5-6814-4b10-893e-de3aa9b68b90"><SHORT-NAME>app_cs_1&…

Android Gradle自定义任务在打包任务执行完成后执行cmd命令

背景 在每次打包之后需要做某事&#xff0c;例如每次打包后我都会安装某个目录下的一个apk。这个apk是通过一堆shell命令过滤得到一个apk的地址&#xff0c;然后把执行的几个shell命令何必成一个alias指令&#xff0c;在打包后只需要执行alias指令实现功能。当然也可以直接写在…

2023AE软件、Adobe After Effects安装步骤分享教程

2023AE软件是一款由Adobe公司开发的视频编辑软件&#xff0c;也被称为Adobe After Effects。它在广告、电影、电视和网络视频等领域广泛应用&#xff0c;用于制作动态图形、特效、合成和其他视觉效果。该软件支持多种视频和音频文件格式&#xff0c;具有丰富的插件和预设&#…

Prometheus结合K8s(二)使用

上一篇介绍了如何搭建 Prometheus结合K8s&#xff08;一&#xff09;搭建-CSDN博客&#xff0c;这章介绍使用 页面访问 kubectl get svc -n prom 看promeheus和granfana的端口访问页面 Prometheus 点击status—target&#xff0c;可以看到metrics的数据来源&#xff0c;即各…

单片机学习笔记 1. 点亮一个LED灯

把基础的东西都过一下&#xff0c;用来学习记录一下。 目录 1、Keil工程 2、Keil实现代码 3、烧录程序 0、实现的功能 点亮一个LED灯 1、Keil工程 打开Keil&#xff0c;Project----New uVision Project&#xff0c;工程文件命名----OK 选择单片机类型AT89C52&#xff0c;和…

Ubuntu安装sublime Tex

Ubuntu安装sublime Text步骤_ubuntu sublime-CSDN博客 Sublime Text 3 - Sublime Text sudo dpkg -i sublime-text_build_3211_amd64.deb

基于FPGA(现场可编程门阵列)的SD NAND图片显示系统是一个复杂的项目,它涉及硬件设计、FPGA编程、SD卡接口、NAND闪存控制以及图像显示等多个方面

文章目录 0、前言 1、目标 2、图片的预处理 3、SD NAND的预处理 4、FPGA实现 4.1、详细设计 4.2、仿真 4.3、实验结果 前言 在上一篇文章《基于FPGA的SD卡的数据读写实现&#xff08;SD NAND FLASH&#xff09;》中&#xff0c;我们了解到了SD NAND Flash的相关知识&am…

Ubuntu24.04LTS设置root用户可远程登录

Ubuntu24.04LTS设置root用户可远程登录 文章目录 Ubuntu24.04LTS设置root用户可远程登录1. 设置root密码2. 设置root用户可远程登录1. 查看ssh服务是否安装2. 安装ssh服务3. 再次查看ssh服务是否安装4. 配置ssh文件5. 重启ssh服务6. root远程登录 1. 设置root密码 Ubuntu安装后…

VM虚拟机装MAC后无法联网,如何解决?

✨在vm虚拟机上&#xff0c;给虚拟机MacOS设置网络适配器。选择NAT模式用于共享主机的IP地址 ✨在MacOS设置中设置网络 以太网 使用DHCP ✨回到本地电脑上&#xff0c;打开 服务&#xff0c;找到VMware DHCP和VMware NAT&#xff0c;把这两个服务打开&#xff0c;专一般问题就…