redux之react-redux实现原理

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组件

完毕!

发布了18 篇原创文章 · 获赞 0 · 访问量 366

猜你喜欢

转载自blog.csdn.net/weixin_39788999/article/details/104124601