Basic knowledge of Redux, partial source code analysis of Redux (handwritten)

Two options for composite component communication:

  • Implement parent-child component communication based on props attributes (or sibling components with the same father)
  • Implement communication between ancestor and descendant components (or parallel components with the same ancestor) based on context context

In addition to the above solutions, communication between components can also be realized based on public state management (Redux)!

In the React framework, we also have solutions for public state management :

  • redux + react-redux
  • dva “redux-saga” or umi
  • MobX

Redux basics

Redux Chinese official website - a state container for JavaScript applications, providing predictable state management. | Redux Chinese official website

What is Redux?
Redux is a state container for JavaScript applications, providing predictable state management!
In addition to being used with React, Redux also supports other frameworks; it is small and compact (only 2kB, including dependencies), but has a very powerful plug-in extension ecosystem!
Redux provides patterns and tools that make it easier for you to understand when, where, why, and how state in your application is updated, and how your application logic will behave when these changes occur!

When should I use Redux?
Redux is more useful when:

  • In a large number of places in the application, there is a large amount of state
  • App status updates frequently over time
  • The logic for updating this state can be complex
  • Applications with medium and large code sizes, developed collaboratively by many people

Redux Library and Tools
Redux is a small standalone JS library, but it is often used together with several other packages:
React-Redux
React-Redux is our official library, which allows React components to interact with Redux and read from the store Get some state and update the store through dispatch actions!
Redux Toolkit
Redux Toolkit is our recommended way of writing Redux logic. It contains packages and functions that we consider essential for building Redux applications. The Redux Toolkit is built on our recommended best practices, simplifying most Redux tasks, preventing common mistakes, and making writing Redux applications easier.
Redux DevTools Extension
The Redux DevTools Extension can display the history of state changes in the Redux store over time, which allows you to effectively debug your application.

The basic workflow of redux is as follows

In addition to the five core operations of redux, we also need some other knowledge to cooperate. All three components need to use the created store container. In the root component, we import the store and put it in the context. Later, other If the component requires it, as long as it is its descendant component, it can be obtained through the context.

practice code

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;

Part of the source code to implement redux by handwriting

Handwrite part of the redux source code yourself (reference path: node_modules/redux/dist/reduxs.js/line 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
    };
};

Guess you like

Origin blog.csdn.net/weixin_42125732/article/details/132188635