redux之react-redux实现原理

2023-11-01

Redux Flow

一、介绍

(一)、redux:
1、reducer念:
reducer就是一个纯函数,接收旧的state和action,返回新的state。
(previousState,action)=>newState;
名字由来:Array.prototype.reduce(reducer,initValue)里的回调函数属于相同的类型。
reduce解释:https://www.runoob.com/jsref/jsref-reduce.html

Redux是JaveScript应用的状态容器。
安装:npm install redux --save
(二)、react-redux
每次都重新调用render和getState太麻烦了,so有了react-redux支持
安装: npm install react-redux --save

提供了两个api:
1、Provider 为后代组件提供store
2、connect 为组件提供数据和变更方法

(三)、异步
Redux只是纯粹的状态管理器,默认只支持同步,实现异步,如延迟,网络请求,就需要中间件的支持:
1、安装:npm install redux-thunk redux-logger --save
2、应用:
store.js

import { createStore, applyMiddleware } from "redux";
import logger from "redux-logger";
import thunk from "redux-thunk";
const store = createStore(fruitReducer,applyMiddleware(logger,thunk));

注:若出现错误类似/babel-preset-reactapp/node_modules/@babel/runtime/helpers/slicedToArray at webpackMissingModule ‘,
就 安装: npm add @babel/runtime

redux原理

  • 存储状态state
  • 获取状态getstate
  • 更新状态dispatch

补充:redux的api中createStore()
接收一个参数:存储状态和方法
接收两个参数:1、存储状态 2、存储方法(处理异步方法)

二、代码实现

下面从零开始搭建项目,手写redux及react-redux源码:

(一)、搭建项目:

  1. 安装官⽅脚⼿架:npm install -g create-react-app
  2. 创建项⽬:create-react-app lesson1
  3. 启动项⽬:npm start

(二)、创建页面:
1、在src(源码)新建文件夹,名为:pages
在pages新建文件:MyReactReduxPage.js(用来查看实现redux的效果展示)

import React, { Component } from "react";
import { connect } from "../myReact-redux";
import { add, minus, asyAdd } from '../action/reactReduxPage';


class MyReactReduxPage extends Component {
    render() {
        console.log("props", this.props);
        const { counter, add, asyAdd, minus } = this.props;
        return (
            <div>
                <h1>MyReactReduxPage</h1>
                <p>counter:{counter}</p>
                <button onClick={add}>add</button>
                <button onClick={asyAdd}>asyAdd</button>
                <button onClick={minus}>minus</button>
            </div>
        );
    }
}

const mapStateToProps = state => {
    return {
        counter: state,
    }
}
const mapDispatchToProps = {
    add,
    minus,
    asyAdd
}
//connect中的参数:state映射和事件映射
export default connect(
    mapStateToProps,//状态映射
    mapDispatchToProps,//派发事件映射
)(MyReactReduxPage);

2、新建action文件夹,用于管理dispatch方法:
./action/reactReduxPage.js

export const add = () => {
    return { type: 'add' }
};
export const minus = () => {
    return { type: 'minus' }
};
//add,minus 没有写dispatch,但是redux最终也是给它执行了dispatch
//dispatch 是因为引入了中间件,再去调用一次dispatch,实现异步
export const asyAdd = () => dispatch => {
    setTimeout(() => {
        dispatch({
            type: "add",
        });
    }, 1000);
}

3、myReact-redux.js

// import React from 'react'
import PropTypes from 'prop-types'
import { bindActionCreators } from './kRedux';
//2、用hooks实现(就是用方法组件)
import React, { useContext, useState, useEffect } from 'react'
const Context = React.createContext();

/*
mapStateToProps 接收一个函数作为参数,直接返回一个函数state;
mapDispatchToProps 是一个对象,所以设置它的初始值为{}
connect 是一个高阶函数,参数是组件,所以要return返回一个组件
*/
export const connect = (mapStateToProps = state => state, mapDispatchToProps = {})=> (WarpComponent) => {
        return class ConnectComponent extends React.Component {
            //class 组件中声明静态的contentTypes可以获取上下文Context
            static contextTypes = {
                store: PropTypes.object
            }
            constructor(props, context) {
                super(props, context)
                this.state = {
                    props: {}
                }
            }
            componentDidMount() {
                const { store } = this.context
                store.subscribe(() => this.update())
                this.update()
            }
            //更新state值得方法
            update() {
                const { store } = this.context
                //state => ({num:state.counter})
                const stateProps = mapStateToProps(store.getState())
                //{add:()=>({type:'add'})}
                // {add:(...args)d=>ispatchEvent(creator(...args))}
                const dispatchProps = bindActionCreators(mapDispatchToProps, store.dispatch)
                this.setState({
                    props: {
                        ...this.state.props,//当前值
                        ...stateProps,//num:state.counter
                        ...dispatchProps //add:(...args)=>dispatch(creator(...args))
                    }
                })
            }
            render() {
                return <WarpComponent {...this.state.props}></WarpComponent>
            }
        }
    }

export class Provider extends React.Component {
    static childContextTypes = {
        store: PropTypes.object
    }
    //class 组件提供的方法 getChildContext
    getChildContext() {
        return { store: this.store }
    }
    constructor(props, context) {
        super(props, context)
        this.store = props.store
    }
    render() {
        return this.props.children
    }
}


//2、用hooks实现(就是用方法组件) --start

export function Provider2(props) {
    return (
        <Context.Provider value={props.store}>
            {props.children}
        </Context.Provider>
    )
}

export const connect2 = (
    mapStateToProps = state => state,
    mapDispatchToProps = {},
) => Cmp => {
    return () => {
        const store = useContext(Context);
        const getProps = () => {
            const stateProps = mapStateToProps(store.getState());
            const dispatchProps = bindActionCreators(
                mapDispatchToProps,
                store.dispatch,
            );
            return {
                ...stateProps,
                ...dispatchProps,
            }
        }
        /*
        hooks 里 的1、useState暴露两个参数[state,setState],
        其中:state--获取值,setstate去设置值
        2、useEffect 设置state的方法,相当于class 里的setState
        */
        const [props, setProps] = useState({ ...getProps() });
        useEffect(() => {
            store.subscribe(() => {
                setProps({ ...props, ...getProps() });
            })
        })
        return <Cmp {...props}/>
    }
}
// 用hooks实现(就是用方法组件) --end

4、myRedux.js
提供了createStore,applyMiddleware(中间件),compose,bindActionCreators方法


export function createStore(reducer, enhancer) {
    //enhancer增强器
    if (enhancer) {
        return enhancer(createStore)(reducer)
    }
    //保存状态:
    let currentState = undefined;
    //回调函数:
    let currentListeners = [];

    function getState() {
        return currentState
    }
    function subscribe(listener) {
        currentListeners.push(listener)
    }
    function dispatch(action) {
        currentState = reducer(currentState, action)
        currentListeners.forEach((v) => v())
        return action;
    }
    dispatch({ type: '@IMOOC/KKB-REDUX' }) //默认执行一遍,才能获取state初始值
    return { getState, subscribe, dispatch }
}
//中间件目的是为了实现异步调用
export function applyMiddleware(...middlewares) {
    //返回强化以后的函数
    return createStore => (...args) => {
        const store = createStore(...args)
        let dispatch = store.dispatch

        const midApi = {
            getState: store.getState,
            dispatch: (...args) => dispatch(...args)
        }
        //使用中间件可以获取状态值、派发action (补充:获取值,action执行方法dispatch更新值)
        const middlewareChain = middlewares.map(middleware => middleware(midApi))

        //compose 可以middlewareChain函数数组合并成一个函数
        dispatch = compose(...middlewareChain)(store.dispatch)
        //返回store,其中dispatch重新修改了,需要进行覆盖store.dispatch的旧值
        return {
            ...store,
            dispatch
        }

    }
}
//compose实现函数的聚合,它返回的是一个函数
export function compose(...funcs) {
    if (funcs.length == 0) {
        return arg => arg  //传入的函数个数为0,就直接返回一个空的函数
    }
    if (funcs.length == 1) {
        return funcs[0]
    }
    return funcs.reduce((left, right) => (...args) => right(left(...args)))
}

function bindActionCreator(creator, dispatch) {
    return (...args) => dispatch(creator(...args))
}
export function bindActionCreators(creators, dispatch) {
    return Object.keys(creators).reduce((ret, item) => {
        ret[item] = bindActionCreator(creators[item], dispatch)
        return ret
    }, {})
}
//另一种写法
export function bindActionCreators2(creators, dispatch) {
    return Object.keys(creators).reduceRight((ret, item) => {
        ret[item] = bindActionCreator(creators[item], dispatch);
        return ret;
    }, {})
}

5、react-redux状态管理:
新建store文件夹:
1)状态值管理: counterReducer.js

export const counterReducer = (state = 0, action) => {
    switch (action.type) {
        case 'add':
            return state + 1;
        case 'minus':
            return state - 1;
        default:
            return state;
    }
}

2)提供中间件方法:thunk,logger
store/myReactReduxStore.js

// import { createStore } from "../kReact-redux";
import { applyMiddleware, createStore } from '../kRedux'
import { counterReducer } from './counterReducer'
//实现异步调用:回调函数重新调用自己
function logger({ dispatch, getState }) {
    return dispatch => action => {
        //中间件任务
        console.log(action.type + '执行了!!!');
        //下一个中间件
        return dispatch(action);
    }
}

const thunk = ({ dispatch, getState }) => dispatch => action => {
    //如果是方法的话:dispatch经过一套工序后在调用,实现异步
    if (typeof action == 'function') {
        return action(dispatch, getState)
    }
    //不是方法就直接调用
    return dispatch(action)
}

// const store = createStore(counterReducer);
const store = createStore(counterReducer, applyMiddleware(logger, thunk));

export default store;

6、测试效果:
1)src/index.js
Provider 是react提供全局的存储值

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { Provider } from './kReact-redux';
import store from './store/myReactReduxStore'

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>
    , document.getElementById('root'));
serviceWorker.unregister();

2)App.js引入MyReactReduxPage组件

完毕!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

redux之react-redux实现原理 的相关文章

  • 函数式组件与类组件有何不同?

    与React类组件相比 React函数式组件究竟有何不同 在过去一段时间里 典型的回答是类组件提供了更多的特性 比如state 当有了Hooks后 答案就不再是这样了 或许你曾听过它们中的某一个在性能上的表现优于另一个 那是哪一个 很多此类
  • 对useReducer的理解

    useReducer是React提供的一个高级Hook 它不像useEffect useState useRef等必须hook一样 没有它我们也可以正常完成需求的开发 但useReducer可以使我们的代码具有更好的可读性 可维护性 可预测
  • React学习之扩展浅比较(三十四)

    注意 这玩意也已经被React PureComponent的功能取代了 这里依旧是提一下 主要是React v15的版本中的react with addons js 这些玩意还存在 哎 害人呐 引入 import shallowCompar
  • 对 React Hook的闭包陷阱的理解,有哪些解决方案?

    hooks中 奇怪 其实符合逻辑 的 闭包陷阱 的场景 同时 在许多 react hooks 的文章里 也能看到 useRef 的身影 那么为什么使用 useRef 又能摆脱 这个 闭包陷阱 搞清楚这些问题 将能较大的提升对 react h
  • 如何在 Ubuntu 20.04 上使用 React 前端设置 Ruby on Rails v7 项目

    作者选择了电子前沿基金会接受捐赠作为为捐款而写程序 介绍 红宝石 on Rails是一个流行的服务器端 Web 应用程序框架 它为当今网络上存在的许多流行应用程序提供支持 例如GitHub Basecamp 声云 Airbnb and Tw
  • 组件间样式覆盖问题--CSS Modules

    1 组件间样式覆盖问题 问题 CityList 组件的样式 会影响 Map 组件的样式 原因 在配置路由时 CityList 和 Map 组件都被导入到项目中 那么组件的样式也就被导入到项目中了 如果组件之间样式名称相同 那么一个组件中的样
  • 一文揭秘饿了么跨端技术的演进、实践与落地

    本文会先带领大家一起简单回顾下跨端技术背景与演进历程与在这一波儿接着一波儿的跨端浪潮中的饿了么跨端现状 以及在这个背景下 相较于业界基于 React Vue 研发习惯出发的各种跨端方案 饿了么为什么会选择走另外一条路 这个过程中我们的一些思
  • 三、react中类组件和函数组件

    简介 本篇我们只要介绍react中类组件与函数组件两种组件的写法 两者的优缺点 同时对在我们的项目开发中该使用类组件还是函数组件进行思考分析 废话不多说进入正题 类组件 设计思路 类组件时面向对象编程的思想 在其中我们去设计类组件时使用st
  • 三分钟实现一个react-router-dom5.0的路由拦截(导航守卫)

    不同于vue 通过在路由里设置meta元字符实现路由拦截 在使用 Vue 框架提供了路由守卫功能 用来在进入某个路有前进行一些校验工作 如果校验失败 就跳转到 404 或者登陆页面 比如 Vue 中的 beforeEnter 函数 rout
  • react组件状态同步-状态提升

    假设定义组件TemperatureInputSon import React from react class TemperatureInputSon extends React Component constructor props su
  • React实现关键字高亮

    先看效果 实现很简单通过以下这个函数 highLight text keyword gt return text split keyword flatMap str gt span keyword span str slice 1 展示某段
  • React class组件、react-hook函数组件分别实现五子棋

    react class类组件 react hook函数组件分别实现五子棋 前言 使用create react app脚手架简单搭建 不用安装其他依赖 纯 js css实现 这里就只是简单的说明目录结构和思路 具体的代码实现请转到 Githu
  • react 父组件调用子组件的方法

    子组件中 const child forwardRef props ref gt useImperativeHandle ref gt 这里面的方法是暴露给父组件的 test console log 我是组件里的test方法 test2 t
  • 【React】 4课 react初识组件

    首先我们如1课创建一个文件夹在文件夹中安装react环境需要的配置文件 npm init y npm i babel standalone D npm i react react dom D 安装配置文件教程链接 https blog cs
  • React配置@src根路径

    第一种 直接修改node modules包中的webpack config js文件 找到node modules react scripts config webpack config js文件 修改其中alias中的配置 添加 src
  • reactJS 干货(reactjs 史上最详细的解析干货)

    一 State和 Props state是状态机 应该包括 那些可能被组件的事件处理器改变并触发用户界面更新的数据 譬如需要对用户输入 服务器请求或者时间变化等作出响应 不应该包括 计算所得数据 React组件 在render 里使用pro
  • React中渲染html结构---dangerouslySetInnerHTML

    dangerouslySetInnerHTML 胡子 语法绑定的内容全部作为普通文本渲染 渲染html结构基于 dangerouslySetInnerHTML dangerouslySetInnerHTML 是 React 标签的一个属性
  • 如何提高React组件的渲染效率的?在React中如何避免不必要的render?

    面试官 说说你是如何提高组件的渲染效率的 在React中如何避免不必要的render 一 是什么 react 基于虚拟 DOM 和高效 Diff 算法的完美配合 实现了对 DOM 最小粒度的更新 大多数情况下 React 对 DOM 的渲染
  • 如何提高React组件的渲染效率的?在React中如何避免不必要的render?

    面试官 说说你是如何提高组件的渲染效率的 在React中如何避免不必要的render 一 是什么 react 基于虚拟 DOM 和高效 Diff 算法的完美配合 实现了对 DOM 最小粒度的更新 大多数情况下 React 对 DOM 的渲染
  • React安装依赖 node_modules中有下载依赖项但package.json文件中没有依赖

    React安装依赖 node modules中有下载依赖项但package json文件中没有依赖 直接在下载依赖项后 加 S 就可以解决 随机 id 生成器 uuid nanoid npm install nanoid S S save

随机推荐

  • 深度

    解码区块链 专题文章三 区块链的安全基础架构及构想 近期 国家发改委明确 区块链 被纳入新基建定义和范围 作为一项能够打通各个技术及领域的基础技术 区块链被认为将在各行业深度融合 新领域拓展 新场景新应用开发等方面潜力无限 解码区块链 内容
  • js 小技巧 ( 根据不同的状态生成不同的颜色和状态 )

    HTML 解决办法 动态绑定 color 然后 根据 三元表达式 进行处理 js 解决办法 动态绑定 color 然后 根据在每个数据的后面添加color属性 可能有的人会问vueb不能用不然不会双向绑定 确实vue要使用vueset 但是
  • 他人工作多年后的总结

    1 找一个好公司 精通至少一门语言及其框架 专注做5到10年 先有深度再有广度 不要为了高工资过早转向管理角色 2 从长远来看 拥有个人项目 阅读 写博客和参加访谈都会有助于你成为一个更好的开发人员 3 成为开发者社区的一部分 努力参加线上
  • qt写C++(引用的妙处,内联函数)

    首先看什么是引用 引用和取地址很像 容易混淆 单独出现 a就是对a这个变量取地址 如果是int a 就是声明这是一个引用 引用 include
  • 基于SpringBoot+redis实现一个简单的点赞功能

    点赞操作比较频繁 而且比较随意 所以数据变更很快 如果用mysql 会对mysql产生很大的压力 于是决定使用Redis 防止数据丢失 所以会定期将数据持久化同步到mysql中 一 Redis 缓存设计及实现 1 1 Redis 安装及运行
  • mysql约束之_外键约束 foreign key

    外键约束 foreign key 创建一个员工表employee 员工编号id 员工的姓名name 部门名称dept name 员工所在的地址address CREATE TABLE employee id INT PRIMARY KEY
  • R语言学习笔记

    参考 W N Venables D M Smith and the R DCT Introduction to R Notes on R A Programming Environment for Data Analysis and Gra
  • java集合Map介绍及具体使用

    目录 Map 双例集合 存储 键值对 key value 的数据 1 基本介绍 2 HashMap 2 1源码介绍 2 2 HashMap源码中的重要常量 2 3面试题 3 LinkHashMap 3 1LinkHashMap源码分析 4
  • Pandas数据处理

    数据预览 首先 调用read excel 方法读取数据 import pandas as pd df pd read excel data xlsx df 姓名 班级 语文 数学 0 张三 一班 99 90 1 李四 一班 78 NaN 2
  • 【从零开始学c++】———模拟实现string类(常用接口的实现)

    string 1 前言 2 string类常用接口实现 3 总结 1 前言 之前学到了string类常用接口的后 我很好奇string类在底层是怎样实现的 在由之前学习到c 有关的之后 所以我打算模拟实现一下string类常用的接口 以便加
  • Allegro学习笔记

    1 用Allegro进行PCB设计 文件的后缀名是 brd 2 双击 brd无法打开该文件 必须在Allegro里面File Open中进行打开
  • 《代码大全2》第5章 软件构建中的设计

    目录 前言 本章主题 5 1 设计中的挑战 5 1 1 设计在软件构建中所处的角色 5 1 2 设计是一个险恶的问题 5 2 关键的设计概念 5 2 1 软件的首要技术使命 管理复杂度 1 管理复杂度的重要性 5 2 2 理想的设计特征 5
  • threeJs着色器ShaderMaterial以及统一着色语言GLSL语法基本介绍

    一 着色器材质ShaderMaterial的基本使用 废话不多讲先来看案例 console log 着色器入门 引入three js import as THREE from three 引入OrbitControls控制器 import
  • RuntimeError: cuDNN error: CUDNN_STATUS_NOT_SUPPORTED.

    原因 batch size太大了 说起来我也觉得挺奇怪 bs太大为什么不报错out of memory而是报了这么一个牛头不对马嘴的错误 不过因为我这个环境已经跑了很多模型都没有问题 因此不是cudnn和cuda版本对应问题 将bs调小一半
  • 常用Linux发行版镜像源配置

    最近研究Linux 试了一些Linux发行版 但是大多数发行版的软件源速度都不是很理想 所以我这里干脆做了一个收集 把我用过的一些常用发行版的软件源设置方法做个总结 大家也可以做个参考 Ubuntu 18 04 18 04是目前Ubuntu
  • unity Animation和Animator动画的暂停和继续播放

    1 利用Animator组件实现暂停和继续播放自己创建的Animation动画 需要按以下顺序操作 在需要播放动画的物体身上添加Animator组件 在Project视图下创建AnimatorController并拖入到Animator组件
  • 【vue3+ts+ant】a-table的column绑定点击事件的两种方式

    需求 给标题这一列的数据绑定点击事件 实现交互 第一种 在dom里面插入标签 并绑定点击事件
  • 【牛客网 - 华为机试 - HJ85 最长回文子串】

    描述 给定一个仅包含小写字母的字符串 求它的最长回文子串的长度 所谓回文串 指左右对称的字符串 所谓子串 指一个字符串删掉其部分前缀和后缀 也可以不删 的字符串 数据范围 字符串长度 进阶 时间复杂度 O n O n 空间复杂度 O n O
  • Java编码规范

    目录 1 代码规范 2 1 1 注释规范 2 1 1 1 页头注释
  • redux之react-redux实现原理

    Redux Flow 一 介绍 一 redux 1 reducer念 reducer就是一个纯函数 接收旧的state和action 返回新的state previousState action gt newState 名字由来 Array