【14】基础知识:React - redux

news2024/11/24 13:56:57

一、 redux理解

1、学习文档

英文文档:https://redux.js.org/

中文文档:http://www.redux.org.cn/

Github: https://github.com/reactjs/redux

2、redux是什么

redux 是一个专门用于做状态管理的 JS 库(不是 react 插件库)。

它可以用在 react,angular,vue等项目中,但基本与react配合使用。

作用:集中式管理 react 应用中多个组件共享的状态。

3、什么情况下需要使用 redux

某个组件的状态,需要让其他组件可以随时拿到(共享)。

一个组件需要改变另一个组件的状态(通信)。

总体原则:能不用就不用,如果不用比较吃力才考虑使用。

4、redux 工作流程

在这里插入图片描述

二、redux 的三个核心概念

1、action

动作的对象,包含 2 个属性:

type:标识属性,值为字符串,唯一,必要属性

data:数据属性,值类型任意,可选属性

例子:{ type: 'ADD_STUDENT', data: {name:'tom',age:18} }

2、 reducer

用于初始化状态、加工状态。

加工时,根据旧的 state 和 action, 产生新的 state 的纯函数。

3、store

将 state、action、reducer 联系在一起的对象

如何得到此对象?

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

此对象的功能?

getState():得到 state

dispatch(action):分发action,触发 reducer 调用,产生新的 state

subscribe(listener):注册监听,当产生了新的 state 时,自动调用

三、redux 的核心 API

四、redux 异步编程

概念:

redux 默认是不能进行异步处理的,某些时候应用中需要在 redux 中执行异步任务(ajax、定时器等)。

明确:

延迟的动作不想交给组件自身,想交给 action。。

何时需要异步 action:

想要对状态进行操作,但是具体的数据靠异步任务返回。

具体编码:

安装异步中间件 redux-thunk :$ yarn add redux-thunk 或者 $ npm install --save redux-thunk ,并在 store 中配置。

创建 action 的函数不再返回一般对象,而是一个函数,该函数中写异步任务。

异步任务有结果后,分发一个同步的 action 去真正操作数据。

备注:异步 action 不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步 action。

五、react-redux

在这里插入图片描述

1、理解

一个 react 插件库

专门用来简化 react 应用中使用 redux

2、 react-redux 将所有组件分成两大类

UI 组件:

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

(2)通过 props 接收数据(一般数据和函数)

(3)不使用任何 redux 的 API

(4)一般保存在 components 文件夹下

容器组件:

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

(2)使用 Redux 的 API

(3)一般保存在 containers 文件夹下

3、相关API

(1)Provider:让所有组件都可以得到 state 数据

<Provider store={store}>
  <App />
</Provider>

(2)connect:用于包装 UI 组件生成容器组件

import { connect } from 'react-redux'

connect(mapStateToprops, mapDispatchToProps)(Counter)

(3)mapStateToprops:将外部的数据(即 state 对象)转换为 UI 组件的标签属性

const mapStateToprops = function (state) {
  return {
    value: state
  }
}

(4)mapDispatchToProps:将分发 action 的函数转换为 UI 组件的标签属性

六、求和案例

在这里插入图片描述

1、纯 react 版

Count 组件,index.jsx

import React, { Component } from 'react'

export default class Count extends Component {
	// 初始化状态
	state = { count: 0 }

	// 加
	increment = () => {
		const { count } = this.state
		const { value } = this.selectNumber
		this.setState({ count: count + value * 1 })
	}

	// 减
	decrement = () => {
		const { count } = this.state
		const { value } = this.selectNumber
		this.setState({ count: count - value * 1 })
	}

	// 奇数再加
	incrementIfOdd = () => {
		const { count } = this.state
		const { value } = this.selectNumber
		if (count % 2 !== 0) {
			this.setState({ count: count + value * 1 })
		}
	}

	// 异步加
	incrementAsync = () => {
		const { count } = this.state
		const { value } = this.selectNumber
		setTimeout(() => {
			this.setState({ count: count + value * 1 })
		}, 500)
	}

	render() {
		const { count } = this.state
		return (
			<>
				<h1>当前求和为:{count}</h1>
				<select ref={c => this.selectNumber = c}>
					<option value="1">1</option>
					<option value="2">2</option>
					<option value="3">3</option>
				</select>&nbsp;
				<button onClick={this.increment}>+</button>&nbsp;
				<button onClick={this.decrement}>-</button>&nbsp;
				<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
				<button onClick={this.incrementAsync}>异步加</button>&nbsp;
			</>
		)
	}
}

2、redux 精简版

(1)安装 redux:$ yarn add redux 或者 $ npm install --save-dev redux

(2)src 目录下建立 redux

新建 store.js

引入 redux 中的 createStor e函数,创建一个 store;createStore 调用时要传入一个为其服务的 reducer;向外暴露 store 对象。

/* 
  该文件专门用于暴露一个 store 对象,整个应用只有一个 store 对象 
*/

// 引入 createStore,专门用于创建 redux 中最为核心的 store 对象
// 新版本中,redux 的 createStore 方法已废弃
// import { createStore } from 'redux'
import { legacy_createStore as createStore } from 'redux'
// 引入为 Count 组件服务的 reducer
import countReducer from './count_reducer'

// 暴露 store
export default createStore(countReducer)

新建 count_reducer.js

reducer 的本质是一个函数,接收:prevState、action,返回加工后的状态;

reducer 有两个作用:初始化状态,加工状态;

reducer 被第一次调用时,是 store 自动触发的;传递的 preState 是 undefined,传递的 action 是:{ type: ‘@@REDUX/INIT_a.2.b.4’ }

/* 
  1、该文件用于创建一个为 Count 组件服务的 reducer,reducer 的本质是一个函数
  2、reducer 函数会接到两个参数,分别为:之前的状态 prevState、动作对象 action
*/
const initState = 0
export default function countReducer(prevState = initState, action) {
  // if (prevState === undefined) prevState = 0
  console.log('prevState-action', prevState, action)

  // 从 action 对象中获取:type、data
  const { type, data } = action

  // 根据 type 决定如何加工数据
  switch (type) {
    case 'increment': // 加
      return prevState + data
    case 'decrement': // 减
      return prevState - data
    default: // 初始化
      return prevState
  }
}

/*
  reducer 初始化值三种写法
  1、default 中直接 return 初始化的值
  2、default 中 return prevState,函数头部判断 prevState===undefined,赋初始化的值
  3、指定形参默认值 prevState = 初始化值
*/

(3)在 Count 组件中使用 redux

import React, { Component } from 'react'
// 引入 store,用于获取 redux 中保存状态
import store from '../../redux/store'

export default class Count extends Component {
	// 初始化状态(自身状态,不包含redux)
	state = { self: '仅为演示' }

	// 每个组件都需要单独引入,改为index.js中统一渲染(示例)
	/* componentDidMount() { // 组件挂载后
		// 监听 redux 中状态的变化,只要变化,就触发视图更新
		store.subscribe(() => { // 订阅 redux 中状态变化
			this.setState({})
		})
	} */

	// 加
	increment = () => {
		const { value } = this.selectNumber
		// 通知 redux 加 value (redux 只负责数据改变,不会触发视图更新)
		store.dispatch({ type: 'increment', data: value * 1 })
	}

	// 减
	decrement = () => {
		const { value } = this.selectNumber
		store.dispatch({ type: 'decrement', data: value * 1 })
	}

	// 奇数再加
	incrementIfOdd = () => {
		const count = store.getState()
		const { value } = this.selectNumber
		if (count % 2 !== 0) {
			store.dispatch({ type: 'increment', data: value * 1 })
		}
	}

	// 异步加
	incrementAsync = () => {
		const { value } = this.selectNumber
		setTimeout(() => {
			store.dispatch({ type: 'increment', data: value * 1 })
		}, 500)
	}

	render() {
		return (
			<>
				<h1>当前求和为:{store.getState()}</h1>
				<select ref={c => this.selectNumber = c}>
					<option value="1">1</option>
					<option value="2">2</option>
					<option value="3">3</option>
				</select>&nbsp;
				<button onClick={this.increment}>+</button>&nbsp;
				<button onClick={this.decrement}>-</button>&nbsp;
				<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
				<button onClick={this.incrementAsync}>异步加</button>&nbsp;
			</>
		)
	}
}

(4)触发页面渲染

redux 只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写。

在 index.js 中监测 store 中状态的改变,一旦发生改变重新渲染 <App/>

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import store from './redux/store'

ReactDOM.createRoot(document.getElementById('root')).render(<App />)

// 订阅 redux 中状态变化,就重新 render 页面(diff算法)
store.subscribe(() => { 
  ReactDOM.createRoot(document.getElementById('root')).render(<App />)
})

3、redux 完整版

通过 action 对象,触发 reducer 调用,产生新的 state

(1)redux 目录下 新增文件:

count_action.js 专门用于创建 action 对象

/*
  该文件专门为 Count 组件生成 action 对象
*/

// 引入定义的常量
import { INCREMENT, DECREMENT } from './constant'

export const createIncrementAction = data => ({ type: INCREMENT, data })
export const createDecrementAction = data => ({ type: DECREMENT, data })

constant.js 放置容易写错的 type 值

/* 
  该模块是用于定义 action 对象中type类型的常量值
  常量一般全大写
  目的是:便于管理的同时防止单词写错 (变量引入,写错编译会报错)
*/
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'

(2)修改 count_reducer.js 中 type 为常量形式

import { INCREMENT, DECREMENT } from './constant'

const initState = 0
export default function countReducer(prevState = initState, action) {
  // 从 action 对象中获取:type、data
  const { type, data } = action

  // 根据 type 决定如何加工数据
  switch (type) {
    case INCREMENT: // 加
      return prevState + data
    case DECREMENT: // 减
      return prevState - data
    default: // 初始化
      return prevState
  }
}

(3)在 Count 组件中使用 actionCreator

import React, { Component } from 'react'
// 引入 store,用于获取 redux 中保存状态
import store from '../../redux/store'
// 引入 actionCreator,专门用于创建 action 对象
import { createIncrementAction, createDecrementAction } from '../../redux/count_action'

export default class Count extends Component {
	// 加
	increment = () => {
		const { value } = this.selectNumber
		// 通知 redux 加 value (redux 只负责数据改变,不会触发视图更新)
		store.dispatch(createIncrementAction(value * 1))
	}

	// 减
	decrement = () => {
		const { value } = this.selectNumber
		store.dispatch(createDecrementAction(value * 1))
	}

	// 奇数再加
	incrementIfOdd = () => {
		const count = store.getState()
		const { value } = this.selectNumber
		if (count % 2 !== 0) {
			store.dispatch(createIncrementAction(value * 1))
		}
	}

	// 异步加
	incrementAsync = () => {
		const { value } = this.selectNumber
		setTimeout(() => {
		 	store.dispatch(createIncrementAction(value * 1))
		}, 500)
	}

	render() {
		return (
			<>
				<h1>当前求和为:{store.getState()}</h1>
				<select ref={c => this.selectNumber = c}>
					<option value="1">1</option>
					<option value="2">2</option>
					<option value="3">3</option>
				</select>&nbsp;
				<button onClick={this.increment}>+</button>&nbsp;
				<button onClick={this.decrement}>-</button>&nbsp;
				<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
				<button onClick={this.incrementAsync}>异步加</button>&nbsp;
			</>
		)
	}
}

4、异步 action 版

(1)store.js 中引入 redux-thunk,并配置

// 引入 createStore,专门用于创建 redux 中最为核心的 store 对象;用于添加中间件
import { legacy_createStore as createStore, applyMiddleware } from 'redux'
// 引入为 Count 组件服务的 reducer
import countReducer from './count_reducer'
// 引入 redux-thunk,用于支持异步 action (返回值为函数)
import thunk from 'redux-thunk'

// 暴露 store
export default createStore(countReducer, applyMiddleware(thunk))

创建异步 action

// 引入定义的常量
// import store from './store'
import { INCREMENT, DECREMENT } from './constant'

// 同步 action,函数返回值 为 Objcet 类型的一般对象。
export const createIncrementAction = data => ({ type: INCREMENT, data })
export const createDecrementAction = data => ({ type: DECREMENT, data })

// 异步 action,函数返回值 为 Function。
// 异步 action 中一般都会调用同步 action,异步 action 不是必须要用的。
export const createIncrementAsyncAction = (data, time) => {
  return dispatch => {
    setTimeout(() => {
      // 此函数本身由 store 调用,不需要再引入 store,通过 store.dispatch() 调用
      // store.dispatch(createIncrementAction(data))

      // 直接调用 return dispatch => {}
      dispatch(createIncrementAction(data))
    }, time)
  }
}

Count 组件中修改为使用异步 Action

// 引入 actionCreator,专门用于创建 action 对象
import { createIncrementAction, createIncrementAsyncAction } from '../../redux/count_action'

export default class Count extends Component {
	...
	// 异步加
	incrementAsync = () => {
		const { value } = this.selectNumber
		// 组件自身等待 500ms
		// setTimeout(() => {
		// 	store.dispatch(createIncrementAction(value * 1))
		// }, 500)

		// 交给 action 等待 500ms
		store.dispatch(createIncrementAsyncAction(value * 1, 500))
	}
}

5、react-redux 的基本使用

(1)安装 react-redux:$ yarn add react-redux 或者 $ npm install --save react-redux

(2)新建 containers 目录,目录下新建 Count 容器组件

connect(mapStateToProps, mapDispatchToProps)(UI组件)

  • mapStateToProps:映射状态,返回值是一个对象
  • mapDispatchToProps:映射操作状态的方法,返回值是一个对象
// 引入 UI 组件 Count
import CountUI from '../../components/Count'
// 引入 connect 用于连接 UI 组件和 redux
import { connect } from 'react-redux'
// 引入 action
import { createIncrementAction, createDecrementAction, createIncrementAsyncAction } from '../../redux/count_action'


/**
 * mapStateToProps 用于传递状态
 * @param {*} state 容器组件本身已经传入了 store,不需要再引入,mapStateToProps 接收的参数就是 state
 * @returns mapStateToProps 函数返回的是一个对象
 * 1、返回的对象中的 key 就作为传递给 UI 组件 props 的 key
 * 2、返回的对象中的 value 就作为传递给 UI 组件 props 的 value
 */
function mapStateToProps(state) {
  return { count: state }
}

/**
 * mapDispatchToProps 用于传递操作状态的方法
 * @param {*} dispatch 容器组件本身已经传入了 store,不需要再引入,mapDispatchToProps 接收的参数就是 dispatch
 * @returns mapDispatchToProps 函数返回的是一个对象
 * 1、返回的对象中的 key 就作为传递给 UI 组件 props 的 key
 * 2、返回的对象中的 value 就作为传递给 UI 组件 props 的 value
 */
function mapDispatchToProps(dispatch) {
  return { // 通知 redux 执行方法
    add: number => dispatch(createIncrementAction(number)),
    reduce: number => dispatch(createDecrementAction(number)),
    addAsync: (number, time) => dispatch(createIncrementAsyncAction(number, time))
  }
}

// 使用 connect()() 创建并暴露一个 Count 的容器组件
export default connect(mapStateToProps, mapDispatchToProps)(CountUI)

(3)向容器组件传入 store:将 Count组件 替换为 Count 容器组件, 并传入 store

// 引入 store
import Count from './containers/Count'
import store from './redux/store'

<Count store={store} />

(4)在 UI 组件 Count 中 使用状态或者操作状态

操作状态:this.props.add()
使用状态:this.props.count

6、react-redux 优化

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

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

相关文章

记一次MySQL5初始化被kill的问题排查 | 京东云技术团队

写在前面 由于测试环境JED申请比较繁琐&#xff0c;所以Eone提供了单机版Mysql供用户使用&#xff0c;近期Eone搭建Mysql5的时候发现莫名被kill了&#xff0c;容器规格是4C8G&#xff0c;磁盘30G 这不科学&#xff0c;之前都是可以的&#xff0c;镜像没变&#xff0c;配置没变…

分享大数据培训班班型

泰迪大数据培训班有VIP就业保障班&#xff0c;项目实战班&#xff0c;技能进阶班&#xff0c;技能提升班。不同班型对应不同学习服务。 一、VIP 就业保障班 二、大数据分析/开发项目实战班 三、技能进阶班 四、技能提升班

RabbitMQ 安装和使用Demo

同步是阻塞 MQ&#xff1a;消息队列&#xff0c;基础数据结构中"先进先出"的数据结构。用来解决应用解耦&#xff0c;异步消息&#xff0c;流量消峰等问题。 RabbitMQ、RocketMQ、Kafka RocketMQ 是阿里的 应用层开发使用RabbitMQ 大数据开发Kafka MQ是不是微服务都…

【MATLAB源码-第49期】基于蚁群算法(ACO)算法的栅格路径规划,输出最佳路径图和算法收敛曲线图。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 蚁群算法是一种模拟自然界蚂蚁觅食行为的启发式优化算法。在蚁群系统中&#xff0c;通过模拟蚂蚁之间通过信息素沟通的方式来寻找最短路径。 在栅格路径规划中&#xff0c;蚁群算法的基本步骤如下&#xff1a; 1. 初始化: …

【Redis】Redis持久化策略

目录 策略RDBbgsave执行流程RDB效果演示RDB优缺点 AOFAOF缓冲区刷新策略AOF的重写机制AOF重写机制的触发机制AOF重写流程 混合持久化同时存在aof和rdb快照&#xff0c;以谁为主&#xff1f;信号 策略 RDB > Redis DataBase 相当于定期的方式实现持久化AOF > Append Only…

Python网络编程改良版客户端

在《Python中套接字实现客户端和服务端3-3》中提到&#xff0c;客户端可以通过connect()方法连接服务端&#xff0c;而连接成功的前提是服务端在必须客户端连接之前就要等待连接。 1 当客户端连接时&#xff0c;服务端没有打开 如果客户端连接服务端时&#xff0c;服务端并没…

通过商品ID查询淘宝商品详情数据,可以拿到商品标题,商品价格,商品库存,商品销量,商品sku数据等,淘宝API接口

通过商品ID查询淘宝商品详情数据可以参考以下步骤&#xff1a; 进入淘宝网站&#xff0c;搜索需要查询的商品ID&#xff0c;例如&#xff1a;652874751412。复制该商品的ID号。点击获取请求key和secret地址&#xff0c;请求参数为num_iid商品ID&is_promotion1。发送请求&a…

企业立案信息API的优势与应用场景

引言 随着科技的不断进步&#xff0c;创业者和企业家们在创办新企业时愈发依赖数字化工具。其中&#xff0c;企业立案信息API成为了一项重要的资源&#xff0c;它提供了有关企业立案的关键信息&#xff0c;为企业家们提供了许多优势和丰富的应用场景。本文将探讨企业立案信息A…

【API篇】四、物理分区算子API

文章目录 1、 分区算子&#xff1a;随机分区2、分区算子&#xff1a;轮询分区3、分区算子&#xff1a;重缩放分区4、分区算子&#xff1a;广播5、分区算子&#xff1a;全局分区6、自定义分区 重分区&#xff0c;即数据"洗牌"&#xff0c;将数据分配到下游算子的并行子…

【日志与守护进程】

文章目录 1 :peach:日志:peach:1.1 :apple:对日志的基本理解:apple:1.2 :apple:对日志系统代码的基本编写:apple:1.2.1 :lemon:messageLog.hpp:lemon:1.2.2 :lemon:tcpServer:lemon: 1.3 :lemon:验证:lemon: 2 :peach:守护进程:peach:2.1 :apple:进程组与会话:apple:2.2 :apple…

Rabbitmq 的管理配置

1、Rabbitmq管理 1.1、多租户与权限 每一个RabbitMQ 服务器都能创建虚拟的消息服务器&#xff0c;我们称之为虚拟主机(virtual host) ,简称为vhost 。每一个vhost 本质上是一个独立的小型RabbitMQ 服务器&#xff0c;拥有自己独立的队列、交换器及绑定关系等&#xff0c;井且它…

用PHP爬取视频代码示例详细教程

以下是一个使用Symfony Panther和PHP进行爬虫的示例程序&#xff0c;用于爬虫企鹅上的视频。请注意&#xff0c;这个示例需要使用https://www.duoip.cn/get_proxy这段代码获取爬虫IP。 <?php // 引入所需的库 require vendor/autoload.php;use Symfony\Component\Panther\P…

多媒体应用设计师 第7章 多媒体数字压缩编码技术基础

1.多媒体数据压缩技术理论基础及压缩编码方法分类 必要性:大数据量的图像信息会给存储器的存储容量&#xff0c;通信线路的带宽&#xff0c;以及计算机的处理速度增加极大压力。如果没有多媒体编码压缩技术的发展&#xff0c;大容量图像&#xff0c;视频信息的存储和传输就难以…

林旅强 | AI+开源时代 - 开发者与治理者的机遇与挑战

点击以下链接收听本期 “大咖访谈” 播客&#xff0c;与大咖面对面&#xff1a; 大咖访谈链接&#xff1a;EP15&#xff1a;林旅强 | AI开源时代 - 开发者与治理者的机遇与挑战 刘天栋&#xff1a;访谈主持&#xff0c;开源雨林顾问&#xff0c;开源社联合创始人&#xff0c;Ap…

人工智能应该怎么学?

人工智能这个词炙手可热&#xff0c;为了跟上时代的步伐&#xff0c;有许多小伙伴就想学习人工智能&#xff0c;今天来介绍一下人工智能究竟是什么&#xff1f;应该怎么学&#xff1f;怎么入门&#xff1f; 首先来看一下什么是人工智能&#xff1f; 人工智能 人工智能 人工智能…

puppeteer学习笔记

目录 介绍启动方法功能一、爬虫优势如何实现爬虫小demo 功能二、执行脚本百度搜索脚本demo 功能三、获取cookie&#xff08;这个只能是模拟浏览器当前进入网页的cookie不是平时用的下载的的浏览器的cookie&#xff09;功能四、监控网页&#xff0c;进行性能分析 介绍 puppetee…

文旅部发布《旅游电子合同管理与服务规范》,推动旅游企业转型升级

日前&#xff0c;文化和旅游部批准发布了旅游电子合同领域的行业标准《旅游电子合同管理与服务规范》&#xff08;以下简称《标准》&#xff09;&#xff0c;将于2023年12月9日起实施。 《标准》聚焦旅游电子合同的适用范围、基本要素、操作规范、载体要求、安全保密等方面进…

【编译原理】对++*p++操作理解

目录 1. 代码段执行结果 2. *p的结合过程 3. 通过汇编代码判断执行结果 1. 代码段执行结果 对于以下代码&#xff1a; int main() { int i 0;int *p &i; *p ; return i; }输出结果为&#xff1a; ASM generation compiler returned: 0 Execution build compiler re…

隐写术--python隐写

0x00 背景 何为隐写术&#xff1f; 隐写术是一类可以隐藏自己写的一些东西的方法&#xff0c;是一门关于信息隐藏的技巧与科学。指的是采取一些不让除预期的接收者之外的任何人知晓信息的传递事件或者信息的内容的方法。 可参考 一文让你完全弄懂Stegosaurus - 知乎 本文要…