【Redux】自己动手实现redux和react-redux

1. React提供context的作用

        在class组件的世界里,如果后代组件共享某些状态,比如主题色、语言键,则需要将这些状态提升到根组件,以props的方式从根组件向后代组件一层一层传递,这样则需要在每层写props.someData,这样使用起来非常麻烦。那么,有没有更好的方式,让后代组件们共享状态?

        当然是有的,react提供了context解决方案,某个组件往context放入一些用于共享的状态,该组件的所有后代组件都可以直接从context取出这些共享状态,跳出一层一层传递的宿命。怎么做?以下给出示例代码

import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';class App extends PureComponent {// 1. 定义静态变量childContextTypes声明context中状态对应的类型static childContextTypes = {lang: PropTypes.string,};constructor(props) {super(props);this.state = {lang: 'zh_CN',};}// 2. 通过getChildContext返回值设置contextgetChildContext() {return {lang: this.state.lang,};}render() {return (<div><SideMenu /><Content /></div>);}
}class SideMenu extends PureComponent {// 1. 定义静态变量contextTypes声明context中状态对应的类型static contextTypes = {lang: PropTypes.string,}constructor(props) {super(props);}// 2. 通过this.context取出contextrender() {return (<div lang={this.context.lang}>123</div>)}
}class Content extends PureComponent {// 1. 定义静态变量contextTypes声明context中状态对应的类型static contextTypes = {lang: PropTypes.string,}constructor(props) {super(props);}// 2. 通过this.context取出contextrender() {return (<div lang={this.context.lang}>123</div>)}
}

总结起来,就两点:

  • 父组件类定义静态变量childContextTypes,通过getChildContext()方法的返回值设置context
  • 后代组类定义静态变量contextTypes,通过this.context取出context
2. redux
2.1 实现createStore

        redux可以比作数据中心,所有数据(即state)存于store中。如果想查询state,必须调用store.getState方法;如果想要更改state,必须调用store.dispatch方法;如果想要在更改state后执行其他额外逻辑,需要使用store.subscribe订阅。由store.subscribe订阅,由store.dispatch发布,构成订阅/发布模式。

        本篇实现一个简易版的createStore,执行createStore()会返回store对象,createStore源码实现如下:

const createStore = (reducer, initialState) => {// 初始化statelet state = initialState;// 保存监听函数const listeners = [];// 返回store当前保存的stateconst getState = () => state;// 通过subscribe传入监听函数const subscribe = (listener) => {listeners.push(listener);}// 执行dispatch,通过reducer计算新的状态state// 并执行所有监听函数const dispatch = (action) => {state = reducer(state, action);for(const listener of listeners) {listener();}}!state && dispatch({});return {getState,dispatch,subscribe,}
}
2.2 Demo

        初始化store(createStore)需要reducer,而reducer可以理解为更新state的方法,是一个纯函数。store.dispatch(action)一个动作后,首先,执行reducer方法,根据action.type找到对应处理逻辑,更新state;然后,执行所有由store.subscribe订阅的监听函数。

        为了验证我们实现的redux功能,设计了一个简单的reducer:

const reducer = (state, action) => {if(!state) {return {menu: {text: 'menu',color: 'red',},}}switch(action.type) {case 'UPDATE_MENU_TEXT':return {...state,menu: {...state.menu,text: action.text,}};case 'UPDATE_MENU_COLOR':return {...state,menu: {...state.menu,color: action.color,}};default:return state;}
}

万事具备,创建store,作为一些简单测试,Demo源码如下:

const store = createStore(reducer);
store.subscribe(() => console.log('demo') );
const action = {type: 'UPDATE_MENU_COLOR',color: 'blue'
};
store.dispatch(action);
// 打印当前状态
console.dir(store.getState());

打印结果如下:

 3. react-redux

        第1节讲到context可以让react组件很方便的共享状态,第2节讲到redux统一管理状态,那是不是可以用redux统一管理react组件的共享状态呢?是可以的,react-redux就实现了context和redux的完美粘合。

3.1 改造父组件

        首先是父组件部分,将父组件类“定义静态变量childContextTypes”和“通过getChildContext()方法的返回值设置context”移到Provider中,并且context的值从Provider的props获取。

import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';export class Provider extends PureComponent {// Provider的props仅含store和childrenstatic propTypes = {store: PropTypes.object,children: PropTypes.any,}// 1. 定义静态变量childContextTypes声明context中状态对应的类型static childContextTypes = {store: PropTypes.object,};// 2. 通过getChildContext返回值设置contextgetChildContext() {return {store: this.props.store}}render() {return (<div>{this.props.children}</div>)}
}
3.2 改造后代组件

        然后是后代组件部分,将后代组“定义静态变量contextTypes”和“通过this.context取出context”移到Connect组件中。

        为什么这里就需要用到高阶组件,而不是像Provider组件一样,仅Connect组件就可以?因为Provider仅提供数据,逻辑简单:1. 用this.props.store设置context,2. 不改变this.props.chidren。但Connect组件不行,这里需要从context取出store,且不能简单将store传递后代组件(后代组件不能从自己的props取store,这会污染后代组件),而是需要从store中取出state和dispatch,根据state和dispatch映射出对应数据(即mapStateToProps和mapDispatchToProps)作为props传给后代组件。怎样映射的方式需要另外方式传进来,即高阶函数connect。

        是怎么做到store.dispatch(action)一个动作后,被connect包裹的后代组件自行渲染?这是因为Connect组件中调用store.subscribe()方法订阅了() => this._updateProps(),this._updateProps中调用了this.setState,来触发更新。

export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => {class Connect extends PureComponent {static contextTypes = {store: PropTypes.object,};constructor(props) {super(props);this.state = {allProps: {}};}componentWillMount() {const { store } = this.context;this._updateProps();store.subscribe(() => this._updateProps());}_updateProps() {const { store } = this.context;const stateProps = mapStateToProps(store.getState(), this.props);const dispatchProps = mapDispatchToProps(store.dispatch, this.props);this.setState({allProps: {...stateProps, // 从store的state取状态数据...dispatchProps, // 需要更新store的state的方法,从这里传入dispatch...this.props // 透传给WrappedComponent}});}render() {return (<WrappedComponent { ...this.state.allProps } />)}}return Connect;
}
3.3 Demo

        Provider作为根组件用来包含App组件,并将store传给Provider。


import React from 'react';
import { createRoot } from 'react-dom/client';import { Provider } from './react-redux';
import createStore, { reducer } from './redux';
import App from './App';const store = createStore(reducer);
const domNode = document.getElementById('root');
const root = createRoot(domNode);
root.render(<Provider store={store}><App /></Provider>
);

         用connect包裹Content组件,通过mapStateToProps和mapDispatchToProps从store中取出相应的数据。


import React from 'react';
import { createRoot } from 'react-dom/client';
import { connect } from './react-redux';class Content extends PureComponent {// 2. 通过this.context取出contextrender() {return (<divlang={this.props.lang}onClick={() => this.props.onChangeLang('zh_CN')}>123</div>)}
}
// state是从store取出来的,props是传给高阶组件Connect的props
const mapStateToProps = (state, props) => {return {lang: state.lang};
}
// dispatch是从store取出来的,props是传给高阶组件Connect的props
const mapDispatchToProps = (dispatch, props) => {return {onChangeLang: (lang) => {dispatch({ type: 'UPDATE_LANG', lang })}}
}
export default connect(mapStateToProps, mapDispatchToProps)(Content);
4. 总结

        react-redux是redux在React的一次成功应用,第2小节redux实现比较简单,主要是对第1小节的理解,理解第1小节后,理解第3小节就容易多了。

        看最新react官方文档,已将PureComponent和Componet列为过时的API,估计后续不推荐再使用class组件,因此react-redux实现方式可能会发生变更,或新出现一种redux和函数组件结合的方式,或基于useContext、useReducer、createContext形成一种新的实现方式。

注:以上,如有不合理之处,还请帮忙指出,大家一起交流学习~  

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

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

相关文章

Java Arrays.copyOfRange的用法

Arrays.copyOfRange的使用方法&#xff1a; 将一个数组拷贝至另一个数组中 参数&#xff1a; original&#xff1a;第一个参数为要拷贝的数组对象 from&#xff1a;第二个参数为拷贝的开始位置&#xff08;包含&#xff09; to&#xff1a;第三个参数为拷贝的结束位置&#x…

django websocket

目录 核心代码 consumers.py from channels.generic.websocket import WebsocketConsumer from channels.exceptions import StopConsumer import datetime import time from asgiref.sync import async_to_sync class ChatConsumer(WebsocketConsumer):def websocket_conne…

ssm基于vue框架和elementui组件的手机官网论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本手机官网就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息&#x…

Linux第3步_安装Ubuntu操作系统

创建好虚拟机后&#xff0c;就可以安装Ubuntu操作系统了。 1、双击“VMware Workstation Pro”&#xff0c;得到下面的界面。 2、点击“编辑虚拟机设置”&#xff0c;见下图&#xff1a; 3、等几秒钟&#xff0c;得到下面的界面&#xff1a; 4、点击“CD/DVD”&#xff0c;得到…

vscode无识别已有的maven java项目(visual studio code not recognizing java project)

文章目录 事情经过尝试疑惑问题解决结论 事情经过 未安装任何Java Extension Pack使用 Maven 的 archetype:generate 命令来创建一个新的项目使用vscode打开了该目录然后安装Java Extension Pack等java插件配置了vscode settings.json中的 java.configuration.runtimes和 java…

Python-1-字符串类型及方法

众所周知&#xff0c;Python面向对象&#xff0c;功能强大 | ू•ૅω•́)ᵎᵎᵎ

mybatis-flex与springBoot整合

mybatis-flex基本使用 1.测试sql2.导入坐标3.框架搭建1.pojo层2.mapper层3.service层4.controller层5.启动类6.配置类7.EmpMapper.xml 4.启动测试 本片文章在springBoot3&#xff0c;jdk21下测试通过 注意官方网站为&#xff1a;https://mybatis-flex.com/ 请点击&#xff1a;直…

使用STM32和ESP8266构建智能家居网络

本文将介绍如何使用STM32微控制器和ESP8266 WiFi模块构建一个智能家居网络。我们将讨论智能家居网络的整体设计思路、硬件连接和软件开发。通过本文的指导和示例代码&#xff0c;读者将能够搭建一个智能家居系统&#xff0c;实现远程控制和数据监测。 一、智能家居网络的整体设…

面试题:你如何理解 System.out.println()?

文章目录 前言首先分析System源码&#xff1a;out源码分析println分析 前言 如果你能自己读懂System.out.println()&#xff0c;就真正了解了Java面向对象编程的含义。 面向对象编程即创建了对象&#xff0c;所有的事情让对象帮亲力亲为&#xff08;即对象调用方法&#xff09…

123基于matlab的差分优化算法优化极限学习机,DE-ELM

基于matlab的差分优化算法优化极限学习机&#xff0c;DE-ELM。输出分类识别结果和准确率。数据可更换自己的&#xff0c;程序已调通&#xff0c;可直接运行。 123差分优化算法极限学习机 (xiaohongshu.com)

深入了解 Vite:快速、简洁、高效的前端构建工具(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

包含自动轮播、点击切换、显示图片信息和页码方框显示码数的 HTML 和 JavaScript 示例:

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>轮播图</title><style>#carousel-conta…

FPGA高端项目:纯verilog的 10G-UDP 高速协议栈,提供7套工程源码和技术支持

目录 1、前言免责声明更新说明 2、相关方案推荐我这里已有的以太网方案本协议栈的千兆网UDP版本1G 千兆网 TCP-->服务器 方案1G 千兆网 TCP-->客户端 方案10G 万兆网 TCP-->服务器客户端 方案 3、该UDP协议栈性能4、详细设计方案设计架构框图网络调试助手GT资源使用GT…

java练习题之String方法运用

应用知识点&#xff1a;​​​​​​String类 1&#xff1a;(String 类)仔细阅读以下代码段&#xff1a; String s "hello"; String t"hello"; char[] c {h,e,l,l,o}; 2&#xff1a;下列选项输出结果为false 的语句是() System.out.println( s.euqals( t…

Leetcode2962. 统计最大元素出现至少 K 次的子数组

Every day a Leetcode 题目来源&#xff1a;2962. 统计最大元素出现至少 K 次的子数组 解法1&#xff1a;滑动窗口 算法如下&#xff1a; 设 mx max⁡(nums)。右端点 right 从左到右遍历 nums。遍历到元素 xnums[right] 时&#xff0c;如果 xmx&#xff0c;就把计数器 co…

Docker安装【学习Docker(十)】Docker SQLServer的安装与卸载、基本命令

文章目录 1. 安装SQL Server拉取镜像运行容器 2. 卸载SQL Server3. 开放端口4. 常用命令5. 使用Navicat Premium连接 1. 安装SQL Server 拉取镜像 sudo docker pull mcr.microsoft.com/mssql/server:2017-latest运行容器 创建挂载点目录 mkdir /home/sqlserver_data运行容器…

漏洞复现--天融信TOPSEC两处远程命令执行

免责声明&#xff1a; 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直…

【Nodejs】基于node http模块的博客demo代码实现

目录 package.json www.js db.js app.js routes/blog.js controllers/blog.js mysql.js responseModel.js 无开发&#xff0c;不安全。 这个demo项目实现了用Promise异步处理http的GET和POST请求&#xff0c;通过mysql的api实现了博客增删改查功能&#xff0c;但因没有…

【KingbaseES】实现MySql函数WEERDAY

CREATE OR REPLACE FUNCTION weekday(date_val date) RETURNS integer AS $$ BEGIN RETURN EXTRACT(ISODOW FROM date_val); END; $$ LANGUAGE plpgsql IMMUTABLE;

智邦国际ERP系统 SQL注入漏洞复现

0x01 产品简介 北京智邦国际软件技术有限公司的ERP系统是一种集成化的企业资源计划&#xff08;Enterprise Resource Planning&#xff0c;简称ERP&#xff09;解决方案&#xff0c;旨在帮助企业实现高效的运营管理和资源优化。 0x02 漏洞概述 智邦国际ERP系统 GetPersonalS…