Redux基础知识,Redux部分源码分析(手写)

news2024/9/28 1:24:50

复合组件通信的两种方案:

  • 基于props属性实现父子组件通信(或具备相同父亲的兄弟组件)
  • 基于context上下文实现祖先和后代组件间的通信(或具备相同祖先的平行组件)

除了以上方案,其实还可以基于公共状态管理(Redux)实现组件间的通信问题!

在React框架中,我们也有公共状态管理的解决方案:

  • redux + react-redux
  • dva「redux-saga 」或 umi
  • MobX

Redux基础知识

Redux 中文官网 - JavaScript 应用的状态容器,提供可预测的状态管理。 | Redux 中文官网

什么是 Redux ?
Redux 是 JavaScript 应用的状态容器,提供可预测的状态管理!
Redux 除了和 React 一起用外,还支持其它框架;它体小精悍(只有2kB,包括依赖),却有很强大的插件扩展生态!
Redux 提供的模式和工具使您更容易理解应用程序中的状态何时、何地、为什么以及如何更新,以及当这些更改发生时,您的应用程序逻辑将如何表现!

我什么时候应该用 Redux ?
Redux 在以下情况下更有用:

  • 在应用的大量地方,都存在大量的状态
  • 应用状态会随着时间的推移而频繁更新
  • 更新该状态的逻辑可能很复杂
  • 中型和大型代码量的应用,很多人协同开发

Redux 库和工具
Redux 是一个小型的独立 JS 库, 但是它通常与其他几个包一起使用:
React-Redux
React-Redux是我们的官方库,它让 React 组件与 Redux 有了交互,可以从 store 读取一些 state,可以通过 dispatch actions 来更新 store!
Redux Toolkit
Redux Toolkit 是我们推荐的编写 Redux 逻辑的方法。 它包含我们认为对于构建 Redux 应用程序必不可少的包和函数。 Redux Toolkit 构建在我们建议的最佳实践中,简化了大多数 Redux 任务,防止了常见错误,并使编写 Redux 应用程序变得更加容易。
Redux DevTools 拓展
Redux DevTools Extension 可以显示 Redux 存储中状态随时间变化的历史记录,这允许您有效地调试应用程序。

redux的基本工作流程如下图

除了redux这五步的核心操作外,我们还需要一些其它的知识做配合,三个组件都需要用到创建的store容器,我们在根组件中,导入store,把他放在上下文中,后期其它组件需要,只要是它的后代组件,都可以通过上下文获取

练习代码

store/index.js

// import { createStore } from '../myredux';
import { createStore } from 'redux';

/* 管理员:修改STORE容器中的公共状态 */
let initial = {
    supNum: 102,
    oppNum: 5
};
const reducer = function reducer(state = initial, action) {
    console.log('state',state);
    console.log('action',action);
    // state:存储STORE容器中的公共状态「最开始没有的时候,赋值初始状态值initial」
    // action:每一次基于dispatch派发的时候,传递进来的行为对象「要求必须具备type属性,存储派发的行为标识」
    // 为了接下来的操作中,我们操作state,不会直接修改容器中的状态「要等到最后return的时候」,我们需要先克隆
    state = { ...state }; //浅拷贝 浅克隆一份 最好是深克隆一下
    // 接下来我们需要基于派发的行为标识,修改STORE容器中的公共状态信息
    switch (action.type) {
        case 'VOTE_SUP':
            state.supNum++;
            break;
        case 'VOTE_OPP':
            state.oppNum++;
            break;
        default:
    }
    // return的内容,会整体替换STORE容器中的状态信息
    return state;
};

/* 创建STORE公共容器 */
const store = createStore(reducer);
export default store;


ThemeContext.js

import React from "react";
const ThemeContext = React.createContext();
export default ThemeContext;


index.jsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import Vote from './views/Vote';
/* 使用ANTD组件库 */
import { ConfigProvider } from 'antd';
import zhCN from 'antd/locale/zh_CN';
import './index.less';
/* REDUX */
import store from './store';
import ThemeContext from './ThemeContext';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <ConfigProvider locale={zhCN}>
        <ThemeContext.Provider
            value={{
                store
            }}>
            <Vote />
        </ThemeContext.Provider>
    </ConfigProvider>
);


Vote.jsx(函数组件)

import React, { useContext, useState, useEffect } from "react";
import './Vote.less';
import VoteMain from './VoteMain';
import VoteFooter from './VoteFooter';
import ThemeContext from "../ThemeContext";


//函数组件用store示例
//把“让组件可以更新”的方法放在公共容器的事件池中! !store.subscribe(函数)
// store.subscribe接收一个方法---这个方法要能让组件更新的放在事件池当中 函数组件只要状态改变就组件更新

const Vote = function Vote() {
    const { store } = useContext(ThemeContext);
    // 获取容器中的公共状态
    let { supNum, oppNum } = store.getState();


    /* // 组件第一次渲染完毕后,把让组件更新的方法,放在STORE的事件池中
    let [num, setNum] = useState(0);
    const update = () => {
        setNum(num + 1);
    };
    useEffect(() => {
        store.subscribe接收一个方法(函数),返回一个方法(函数)
         let unsubscribe = store.subscribe(让组件更新的方法)
           + 把让组件更新的方法放在STORE的事件池中
           + 返回的unsubscribe方法执行,可以把刚才放入事件池中的方法移除掉
        let unsubscribe = store.subscribe(update);
        return () => { //会在上一次销毁的时候执行
            unsubscribe(); //方法执行,可以把刚才放入事件池中的方法移除掉
        };
    }, [num]); */
    //每次修改为随机数,第一次渲染完成后
    let [_, setNum] = useState(0);
    useEffect(() => {
        store.subscribe(() => {
            setNum(+new Date());
        });
    }, []);

    return <div className="vote-box">
        <div className="header">
            <h2 className="title">React是很棒的前端框架</h2>
            <span className="num">{supNum + oppNum}</span>
        </div>
        <VoteMain />
        <VoteFooter />
    </div>;
};

export default Vote;

VoteFooter.jsx(函数组件)

import React, { useContext } from "react";
import { Button } from 'antd';
import ThemeContext from "../ThemeContext";

//函数组件用store示例

const VoteFooter = function VoteFooter() {
    const { store } = useContext(ThemeContext);

    return <div className="footer">
        <Button type="primary"
            onClick={() => {
                store.dispatch({
                    type: 'VOTE_SUP',
                    stpe:10,
                });
            }}>
            支持
        </Button>

        <Button type="primary" danger
            onClick={() => {
               store.dispatch({
                    type:'VOTE_OPP',
               })
            }}>
            反对
        </Button>
    </div>;
};
export default VoteFooter;


VoteMain.jsx(类组件)

import React from "react";
import ThemeContext from "../ThemeContext";

//类组件用store示例
// store.subscribe接收一个方法---这个方法要能让组件更新的放在事件池当中

class VoteMain extends React.Component {
    static contextType = ThemeContext;

    render() {
        const { store } = this.context; //上下文信息存在this.context
        let { supNum, oppNum } = store.getState();
        let ratio = '--',
            total = supNum+oppNum;
        if(total>0) ratio = (supNum / total *100).toFixed(2) + '%';

        return <div className="main">
            <p>支持人数:{supNum}人</p>
            <p>反对人数:{oppNum}人</p>
            <p>比率:{ratio}</p>
        </div>;
    }

    //类组件第一次渲染完
    componentDidMount() {
        const { store } = this.context;
        store.subscribe(() => {
            this.forceUpdate(); //这个方法的执行 能类组件更新---强制更新
        });
    }
}

export default VoteMain;

手写实现redux的部分源码

自己手写redux部分源码(参考路径:node_modules/redux/dist/reduxs.js/130行)

import _ from './assets/utils';

/* 实现redux的部分源码 */
//源码路径node_modules/redux/dist/reduxs.js/130行
export const createStore = function createStore(reducer) {
    if (typeof reducer !== 'function') throw new Error("Expected the root reducer to be a function");

    let state, //存放公共状态
        listeners = []; //事件池

    /* 获取公共状态 */
    const getState = function getState() {
        // 返回公共状态信息即可
        return state;
    };

    /* 向事件池中加入让组件更新的方法 */
    const subscribe = function subscribe(listener) {
        // 规则校验
        // 1.必须是个函数
        // 2.去重处理,看传进来的是否在事件池中,不在的话再加入事件池includes判断
        if (typeof listener !== "function") throw new TypeError("Expected the listener to be a function");
        // 把传入的方法(让组件更新的办法)加入到事件池中「需要做去重处理」
        if (!listeners.includes(listener)) {
            listeners.push(listener);
        }
        // 返回一个从事件池中移除方法的函数
        return function unsubscribe() {
            let index = listeners.indexOf(listener); //获取它的索引
            listeners.splice(index, 1); //从当前索引位置 删除一项
        };
    };

    /* 派发任务通知REDUCER执行 */
    const dispatch = function dispatch(action) {
        // 规则校验
        if (!_.isPlainObject(action)) throw new TypeError("Actions must be plain objects");
        if (typeof action.type === "undefined") throw new TypeError("Actions may not have an undefined 'type' property");

        // 把reducer执行,传递:公共状态、行为对象;接收执行的返回值,替换公共状态;
        state = reducer(state, action);

        // 当状态更改,我们还需要把事件池中的方法执行
        listeners.forEach(listener => {
            listener();
        });

        return action;
    };

    /* redux内部会默认进行一次dispatch派发,目的:给公共容器中的状态赋值初始值 */
    const randomString = () => Math.random().toString(36).substring(7).split('').join('.');
    dispatch({
        // type: Symbol() //ES6语法 Symbol()唯一值
        type: "@@redux/INIT" + randomString()
    });

    // 返回创建的STORE对象
    return {
        getState,
        subscribe,
        dispatch
    };
};

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

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

相关文章

有哪些pdf修改方法?这几种方法学会就够了

有哪些pdf修改方法&#xff1f;PDF是一种非常常见的电子文档格式&#xff0c;它有很多优点&#xff0c;例如可读性强、易于保护、易于打印等等。但是&#xff0c;有时候我们需要对PDF进行修改&#xff0c;例如添加、删除或修改文本、更改图片、合并或分割文件等等。那么今天就给…

对强缓存和协商缓存的理解

浏览器缓存的定义&#xff1a; 浏览器缓存是浏览器在本地磁盘对用户最近请求过的文档进行存储&#xff0c;当访问者再次访问同一页面时&#xff0c;浏览器就可以直接从本地磁盘加载文档。 浏览器缓存分为强缓存和协商缓存。 浏览器是如何使用缓存的&#xff1a; 浏览器缓存…

天津市城市管理委员会莅临道本科技,共同探讨加快推进城市综合执法数字化新模式

2023年8月4日&#xff0c;市城管委处长李春利带队莅临道本科技考察指导&#xff0c;与道本科技董事长王智勇共同探讨加快推进城市综合执法数字化新模式。 会议上&#xff0c;董事长王智勇着重介绍了道本科技最新研发上线的法治大数据应用产品“合规数知法用法平台”。他表示&am…

微信开发之检测僵尸粉的技术实现

简要描述&#xff1a; 检测好友状态 请求URL&#xff1a; http://域名地址/checkZombie 请求方式&#xff1a; POST 请求头Headers&#xff1a; Content-Type&#xff1a;application/jsonAuthorization&#xff1a;login接口返回 参数&#xff1a; 参数名必选类型说明…

《算法和数据结构》算法篇

前言 我大学的时候比较疯狂&#xff0c;除了上课的时候&#xff0c;基本都是在机房刷题&#xff0c;当然&#xff0c;有时候连上课都在想题目&#xff0c;纸上写好代码&#xff0c;一下课就冲进机房把代码敲了&#xff0c;目的很单纯&#xff0c;为了冲排行榜&#xff0c;就像玩…

C++ 派生类成员的标识与访问——作用域分辨符

在派生类中&#xff0c;成员可以按访问属性分为以下四种&#xff1a; &#xff08;1&#xff09;不可访问成员。这是从基类私有成员继承下来的&#xff0c;派生类或是建立派生类对象的模块都无法访问到它们&#xff0c;如果从派生类继续派生新类&#xff0c;也是无法访问的。 &…

OpenLayers入门,OpenLayers视图飞行动画,OpenLayers飞行到指定经纬度位置

专栏目录: OpenLayers入门教程汇总目录 前言 本章实现OpenLayers视图飞行动画,根据经纬度和动画持续时长,飞行到指定地图位置。 上一章中可以直接通过修改中心点和层级跳转到指定位置:《Openlayers入门,Openlayers调整中心点坐标、Openlayers调整缩放级别、Openlayers调…

第八章:Linux信号

系列文章目录 文章目录 系列文章目录前言linux中的信号进程对信号的处理信号的释义 信号的捕捉信号的捕捉signal()信号的捕捉sigaction() 信号的产生通过终端按键产生信号前台进程与后台进程 kill()用户调用kill向操作系统发送信号raise()进程自己给自己发任意信号&#xff08;…

Qt事件过滤器

1 介绍 事件过滤器是一种机制&#xff0c;当某个QObject没有所需要的事件功能时&#xff0c;可将其委托给其它QObject&#xff0c;通过eventFilter成员函数来过滤实现功能。 2 主要构成 委托&#xff1a; ui->QObject1->installEventFilter(QObject2); eventFilter声明 …

【C++精华铺】4.C++类和对象(上)面向对象、类、this指针

目录 1. 面向过程和面向对象 2. 类的引入 3. 类的定义 4. 类的访问限定符和封装 4.1 类的访问限定符 4.2 封装 5. 类的作用域 6. 类的实例化 7. 类对象模型 7.1 类对象的存储方式 7.2 类的大小 7.2.1 空类的大小 7.2.2 结构体内存对齐规则 8. this关键字深入讲解 8.1…

如何选择适合自己的考试培训系统

随着考试的逐渐增多和竞争的加剧&#xff0c;许多人开始关注考试培训系统&#xff0c;以提高他们的考试成绩。然而&#xff0c;选择适合自己的考试培训系统并不容易&#xff0c;因为市场上有许多不同的培训系统可供选择。 1. 确定目标 在选择培训系统之前&#xff0c;首先要明…

Java并发编程(一)多线程基础概念

概述 多线程技术&#xff1a;基于软件或者硬件实现多个线程并发执行的技术 线程可以理解为轻量级进程&#xff0c;切换开销远远小于进程 在多核CPU的计算机下&#xff0c;使用多线程可以更好的利用计算机资源从而提高计算机利用率和效率来应对现如今的高并发网络环境 并发编程…

无涯教程-Perl - getpriority函数

描述 此函数返回进程(PRIO_PROCESS),进程组(PRIO_PGRP)或用户(PRIO_USER)的当前优先级。 参数WHICH指定要为PRIO_PROCESS,PRIO_PGRP或PRIO_USER之一设置优先级的实体,WHO是要设置的进程ID或用户ID。 WHO的值为0定义了当前流程,流程组或用户。这会在不支持系统getpriority()函…

C++STL简介:提升编程效率与可维护性的利器

目录 引言 一、STL基本概念 二、具体介绍 2.1 容器 2.2 算法 2.2.1 排序&#xff08;Sort&#xff09; 2.2.2 查找&#xff08;Find&#xff09; 2.3.2 复制&#xff08;Copy&#xff09; 2.3 迭代器 三、分析STL 优势&#xff1a; 缺点&#xff1a; 如何学习&#…

《合成孔径雷达成像算法与实现》Figure3.7

代码复现如下&#xff1a; clc clear all close all%参数设置 TBP 100; %时间带宽积 T 10e-6; %脉冲持续时间%参数计算 B TBP/T; …

做BI领域的ChatGPT,思迈特升级一站式ABI平台

8月8日&#xff0c;以「指标驱动 智能决策」为主题&#xff0c;2023 Smartbi V11系列新品发布会在广州丽思卡尔顿酒店开幕。 ​ 后疫情时代&#xff0c;BI发展趋势的观察与应对 在发布会上&#xff0c;思迈特CEO吴华夫在开场致辞中表示&#xff0c;当前大环境背景下&#xf…

行业追踪,2023-08-09

自动复盘 2023-08-09 凡所有相&#xff0c;皆是虚妄。若见诸相非相&#xff0c;即见如来。 k 线图是最好的老师&#xff0c;每天持续发布板块的rps排名&#xff0c;追踪板块&#xff0c;板块来开仓&#xff0c;板块去清仓&#xff0c;丢弃自以为是的想法&#xff0c;板块去留让…

讯飞星火大模型无需代码连接飞书即时消息的方法

1 使用场景 用户联络需求的升级&#xff0c;使智能客服与智能营销迎来飞速增长&#xff0c;客户服务也成为企业经营中非常重要的一环&#xff0c;如何提高客户服务质量和响应速度&#xff0c;一直是企业需要解决的难题。人工智能产品&#xff0c;也正在成为越来越多企业提升业务…

flutter3.0项目集成高得地图

文章目录 1.创建应用:注册高得开发平台 安卓端的设置flutter的集成ios端的设置 先看一下代码运行结果 安卓端真机 ios端真机屏幕录屏 本篇文章demo下载地址 下载后请用xcode修改自己的ios证书真机运行.flutter代码在PG1.dart里面修改 String androidKey “b3392bb7fe532b0eb…

Mac终端利器:Homebrew + iTerm2 + Oh My Zsh 教程

引言 前段时间调整了一下 iTerm2 的环境&#xff0c;感觉比以前好看多了&#xff0c;并且更加高效&#xff0c;这里做一个记录&#xff0c;希望能给大家一些启发。 工具介绍 brew&#xff1a;Mac OS 下强大的包管理工具。iTerm2&#xff1a;iTerm2是 Mac OS 终端的替代品&am…