CcbeanBlog CcbeanBlog
首页
  • 前端文章

    • JavaScript
    • HTML+CSS
    • Vue
    • React
  • 系列笔记

    • React使用学习
    • Vue2源码探究
  • Node文章

    • 基础
    • 问题
    • 框架
  • 系列笔记

    • 数据结构与算法
  • 构建工具文章

    • webpack
  • 系列笔记

    • Webpack5使用学习
  • MySQL
  • Linux
  • 网络
  • 小技巧
  • 杂记
  • 系列笔记

    • Protobuf Buffers
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Ccbean

靡不有初,鲜克有终
首页
  • 前端文章

    • JavaScript
    • HTML+CSS
    • Vue
    • React
  • 系列笔记

    • React使用学习
    • Vue2源码探究
  • Node文章

    • 基础
    • 问题
    • 框架
  • 系列笔记

    • 数据结构与算法
  • 构建工具文章

    • webpack
  • 系列笔记

    • Webpack5使用学习
  • MySQL
  • Linux
  • 网络
  • 小技巧
  • 杂记
  • 系列笔记

    • Protobuf Buffers
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • JavaScript

  • HTML+CSS

  • Vue

  • React

    • Redux原理学习
    • Apollo-Client简介
    • React零碎点总结
    • react实现按需加载-代码分割(上)
    • react实现按需加载-代码分割(下)
    • 构建自己的redux
    • TypeScript

    • 系列笔记

    • 前端
    • React
    ccbean
    2017-12-29
    目录

    构建自己的redux

    # 构建自己的redux

    # 前言:

    redux的基本概念:

    http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_one_basic_usages.html (opens new window)

    # 一、构建自己的redux

    在redux中,对外主要提供createStore和applyMiddleware两种方法,具体的代码块如下:

    创建piggy-redux.js

    // 片段一
    export const createStore = (reducer,enhancer) => {
        // 如果有中间件,那么就用中间件包裹一层
        if(enhancer){
            return enhancer(createStore)(reducer);
        }
    
        let currentState = {};
        let currentListeners = [];
    
        const getState = () => currentState;
        const subscribe = listener => {
            currentListeners.push(listener);
        }
        const dispatch = action => {
            currentState = reducer(currentState,action);
            currentListeners.map(v => v());
            return action;
        }
    
        // 初始化的时候getState就能获取到值,先执行下dispatch
        dispatch({type:'@@piggy-redux'});
        return {getState,subscribe,dispatch};
    }
    

    redux提供createStore来返回一个对象,对象中包含的方法有:

    • getState来获取此时的state状态
    • subscribe来设置监听函数
    • dispatch来发出view中的action到reducer,dispatch会自动操作reducer,所以reducer需要直接传进来;同时也调用了subscribe的函数,所以每次dispatch的时候被监听的函数都会被执行
    // 片段二 为actionCreater包裹一层dispatch  此方法是在react-redux中使用
    // 将actionCreater包裹dispatch 以实现dispatch发送action
    export const  bindActionCreaters = (creaters,dispatch) => {
        // 写法一
        // let bound = {};
        // Object.keys(creaters).forEach(v => {
        //    let creater = creaters[v];
        //    bound[v] = bindActionCreater(creater,dispatch);
        // })
        // return bound;
    
        // 写法二
        return Object.keys(creaters).reduce((ret,item) => {
            ret[item]  = bindActionCreater(creaters[item],dispatch);
            return ret;
        },{})
    }
    
    // 调用一个actioncreater函数如:addGun(…参数),经过函数包裹后就是dispatch(addGun(...参数)) (…args)是参数透传,不理解creater中的参数怎么传过去的
    const bindActionCreater = (creater,dispatch) => (...args) => dispatch(creater(...args))
    
    // 片段三 应用中间件
    // 应用中间件
    export const applyMiddleware = (...middleWares) => {
        return createStore => (...args) => {
            const store = createStore(...args);
            let dispatch = store.dispatch;
    
            const midApi = {
                getState:store.getState,
                dispatch:(...args) => dispatch(...args)
            }
    
            // 多个中间件
            const middleWareChain = middleWares.map(middleWare => middleWare(midApi))
            dispatch = compose(...middleWareChain)(store.dispatch);
    
            return {
                ...store,
                dispatch
            };
        }
    }
    
    const compose = (...funcs) => {
        if(funcs.length === 0) return func => func;
        if(funcs.length === 1) return funcs[0];
        return funcs.reduce((ret,item) => (...args) => ret(item(...args)))
    }
    

    # 二、构建react-redux

    使用react-redux可以在react中优雅地使用redux。首先引入需要使用的类:

    import React from 'react';
    import PropTypes from 'prop-types’;
    import {bindActionCreaters} from './piggy-redux';
    

    react-redux主要提供两个方法,

    • 一个是Provider:把store放到context中,所有的子元素可以直接取到store;
    • 另一个是connect:负责连接组件,将redux里的数据放到组件的属性里。作用是负责接受一个组件,将一些state放入进去;数据变化时,能够通知组件
    // Provider
    export class Provider extends React.Component{
        static childContextTypes = {
            store: PropTypes.object
        }
    
        getChildContext(){
            return {store:this.store};
        }
    
        constructor(props,context){
            super(props,context);
            this.store = props.store;
        }
    
        render(){
            return this.props.children;
        }
    }
    
    export const connect = (mapStateToProps = state => state,mapDispatchToProps = {}) => WrapComponent => {
        return class ConnectComponent extends React.Component{
            static contextTypes = {
                store:PropTypes.object
            }
            constructor(props,context){
                super(props,context);
                this.state = {
                    props:{}
                }
            }
    
            componentDidMount(){
                const {store} = this.context;
                // 在写createStore的时候一直不明白什么时候订阅的,是谁订阅的,写到这里突然明白了,是connect订阅的,确实很优雅
                store.subscribe(() => this.update())
    
                this.update();
            }
    
            update(){
                // 获取mapStateToProps和mapDispatchToProps将其更新到props中,使被connect包裹的组件可以直接使用store中的数据
                const {store} = this.context;
                const stateProps = mapStateToProps(store.getState());
                // state可以直接传入,但是action不能直接传入,action需要diapatch把它传给store。action的生成是通过acticnCreater生成的,所以用dispatch把actionCreater包裹一层即可
                // 将actionCreater包裹一层dispatch;同时函数内部也应该具有dispatch功能,所有把dispatch传进去  实现此功能的函数是redux给的,所以在redux中实现此函数
                const dispatchProps = bindActionCreaters(mapDispatchToProps,store.dispatch);
                this.setState({
                    props:{
                        ...this.state.props,
                        ...stateProps,
                        ...dispatchProps
                    }
                });
            }
    
            render(){
                return <WrapComponent {...this.state.props}></WrapComponent>
            }
        }
    }
    

    # 三、使用它们

    两个简单的中间件

    // 便于理解上面的中间件应用
    const thunk  = ({dispatch,getState}) => next => action => {
        if(typeof action === 'function'){
            return action(dispatch,getState);
        }
    
    
        return next(action);
    }
    
    export default thunk;
    
    /*--------------------------------------------*/
    const arraythunk  = ({dispatch,getState}) => next => action => {
        if(Array.isArray(action)){
            // 如果是数组,遍历dispatch出数据
            return action.forEach(v => dispatch(v))
        }
    
        return next(action);
    }
    
    export default arraythunk;
    

    使用:

    // action type
    const ADD_GUN = 'ADD_GUN';
    const REMOVE_GUN = 'REMOVE_GUN';
    
    // reducer
    export const counter = (state = 0,action) => {
        switch (action.type){
            case 'ADD_GUN':
                return state+1;
            case 'REMOVE_GUN':
                return state-1;
            default:
                return 10;
        }
    }
    
    // action creater
    export const addGun = () => {
        return {type:ADD_GUN}
    }
    export const  removeGun = () => {
        return {type:REMOVE_GUN}
    }
    
    export const twiceGun = () => {
        return [{type:ADD_GUN},{type:ADD_GUN}];
    }
    
    export const removeGunAsync = () => (
        dispatch => setTimeout(() => {
            dispatch(removeGun())
        },2000)
    )
    
    import React from 'react';
    import {connect} from './piggy-react-redux'
    import {addGun,removeGun,removeGunAsync,twiceGun} from './react.redux'
    import {createSelector} from 'reselect';
    
    // reselect是对redux进行优化的库
    const numSelector = createSelector(
        state => state,
        // 第二个函数的参数,是第一个的返回值
        state => ({num:state*2})
    );
    
    
    /*
    *  使用装饰器优化connect的代码
    * */
    @connect(
        state => numSelector(state),
        {addGun,removeGun,removeGunAsync,twiceGun}
    )
    
    class App extends React.Component{
    
        render(){
            return(
                <div>
    
                    <h1>现在有{this.props.num}把枪</h1>
                    <button onClick={this.props.addGun}>申请武器</button>
                    <button onClick={this.props.removeGun}>上交武器</button>
                    <button onClick={this.props.removeGunAsync}>等会上交</button>
                    <button onClick={this.props.twiceGun}>申请两把</button>
                </div>
            );
        }
    }
    
    // const mapStateToProps = (state) => {
    //     return {
    //         num : state
    //     }
    // }
    
    // 如果action的方法名和在组件中使用的名称不一致,可以这样写,不过一般情况下都是保持一致的
    // const mapDispatchToProps = (dispatch) => {
    //     return {
    //         addGun: () => {
    //             dispatch(addGun())
    //         },
    //         removeGun: () => {
    //             dispatch(removeGun())
    //         },
    //         removeGunAsync: () => {
    //             dispatch(removeGunAsync())
    //         }
    //     }
    // }
    
    // 如果一致,就可以进行简写
    // const mapDispatchToProps = {addGun,removeGun,removeGunAsync};
    
    /*
    * connect接收两个参数,一个mapStateToProps,就是把redux的state,
    * 转为组件的Props,还有一个参数是mapDispatchToprops,就是把
    * dispatch(actions)的方法,转为Props属性函数。
    * 也就是说,第一个参数是:你要state中的什么属性放到组件的props中
    * 第二个参数是:你要actions中的什么方法放到组件的props中,并自动dispatch
    *
    * 下面的connect写法是标准的装饰器写法,就是connect先执行,然后再把App
    * 当做参数传进来
    * */
    // App = connect(mapStateToProps,mapDispatchToProps)(App);
    
    
    export default App;
    
    编辑 (opens new window)
    上次更新: 2021/11/10, 12:11:50
    react实现按需加载-代码分割(下)
    TS入门概念速记

    ← react实现按需加载-代码分割(下) TS入门概念速记→

    最近更新
    01
    阅读精通正则表达式总结
    09-29
    02
    项目搭建规范的配置
    07-15
    03
    Vite的使用
    07-03
    更多文章>
    Theme by Vdoing | Copyright © 2018-2023 Ccbeango
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式