redux源码简单实现

2022-10-25 13:59:25 浏览数 (2)

redux核心方法createStore

实现dispatch,getStore,subscribe等api

帮助方法

简单实现applyMiddleWare,combinReducers,bindActionCreatores,

中间件

thunk,logger

个人心得:此版进行浅比较,store未改变就不执行subscrube,瞎玩,store初始值要是一个简单对象

使用方法

代码语言:javascript复制
import React, { Component } from 'react'
import store from './../store'
export default class MyReduxPage extends Component {
    add = () => {
        const { dispatch } = store;
        // dispatch({ type: 'ADD', payload: 10 })
        dispatch(() => {
            return { type: 'ADD', payload: 10 }
        })
    }
    sub = () => {
        const { dispatch } = store;
        dispatch({ type: 'SUB', payload: 10 })
    }
    addAge = () => {
        const { dispatch } = store;
        dispatch({ type: 'ADDAGE', payload: 40 })
    }
    componentDidMount() {
        // 订阅事件
        this.unSubscribe = store.subscribe(() => {
            // 强制更新
            this.forceUpdate()
        })
    }
    componentWillUnmount() {
        // 取消订阅
        this.unSubscribe();
    }
    render() {
        const { add, sub, addAge } = this;
        console.log(4444)
        return (
            <div>
                <h2>这厮是store</h2>
                <div>{store.getStore().currentReducer.count}</div>
                {/* 点击按钮就可以触发store.dispatch,然后调用reducer,改变store的值,但是,页面早已经渲染完了,页面展示的是初始的值,不会重新render */}
                <button onClick={add}>点击 10</button>
                {/* 所以我们必须订阅一个事件,相当于在改变数据后的回调,因为reducer执行完,会遍历subscribe事件 */}
                <button onClick={sub}>点击-10</button>
                <h3>这是age Store</h3>
                <div>{store.getStore().ageReducer.age}</div>
                <button onClick={addAge}>点击 40</button>
                <button onClick={sub}>点击-10</button>
                <br />
                <button onClick={() => store.dispatch({ type: 'ssssss' })}>
                    dispatch个无操作的值
                </button>
            </div>
        )
    }
}

简单实现方法

代码语言:javascript复制
// createStore方法
function createStore(reducer, enhancer) {
    if (enhancer) {
        // 如果应用了中间件,就要加强dispatch
        return enhancer(createStore)(reducer)
    }
    // 定义一个store undefined
    let store;
    // 存储订阅事件
    let subscrition = []
    // 获取store方法
    function getStore() {
        return store
    }
    // 触发reducer方法
    function dispatch(action) {
        // 改变store后
        const newStore = reducer(store, action);
        // 触发订阅事件
        if (newStore !== store) {
            store = newStore;
            subscrition.forEach(update => update())
        }

    }
    // 订阅方法,返回一个取消订阅函数,有来有去,节约土地
    function subscribe(update) {
        subscrition.push(update)
        return () => {
            subscrition = subscrition.filter(subscribe => subscribe !== update)
        }
    }
    // 手动触发一下dispatch,将用户的初始值,赋值给store
    // 首次store无值,所以取默认值
    // reudcer(store=9,action)
    dispatch({ type: '@abdcdddsfasf' })
    return {
        getStore,
        dispatch,
        subscribe
    }
}
// combineReducers 为了更好的实现reducer模块化,合并多个reducer方法
// 接收一个redcueObj,返回一个reducer函数
// 勿忘初心嘛1返回一个reducer函数
function combineReducers(reducerObj) {
    // 获取reducer key值集合
    const reducerKeys = Object.keys(reducerObj);


    return (store = {}, action) => {
        const nextStore = {};
        // store是否改变了标识
        let changed = false;
        // 触发一个dispatch 遍历执行所有的reducer,因为reducer是纯函数嘛,没合适的类型返回原值
        // 宁可错杀一万,不能放过一个的策略,挨个触发一下
        reducerKeys.forEach(key => {
            // 获取当前reducer
            const currentReducer = reducerObj[key];
            // 获取当前reducer对应的store
            const currentStore = store[key];
            // 执行这个reducer,传入这个reducer对应的store和action
            nextStore[key] = currentReducer(currentStore, action);
            // 改变标识,如果当前store不等于下次的store,标识为true
            changed = changed || nextStore[key] !== currentStore;
        })
        // store对象里属性值得数量变化也是变化
        changed = changed || Object.keys(nextStore).length !== Object.keys(store).length;
        // 改变了?返回新的,否则返回老的
        console.log(nextStore , 'store')
        return changed ? nextStore : store;
    }
}


// 为了满足复杂的业务场景,提升dispatch能力,让他不单纯的只会处理对象,也让能处理函数或promise等等,
// 应用中间件的方法,可能是多个,...接收
function applyMiddleWare(...middleWare) {
    // 返回一个函数,这个函数做createStoer要干的活
    // 所以传进来,加强dispatch
    return createStore => reducer => {
        // 这事不能不干,
        let store = createStore(reducer);
        // 下面开始加强store

        // 首先给中间传入他们可能用到的参数
        // dispatch,getStore
        let dispatch = store.dispatch;
        let api = {
            getStore: store.getStore,
            dispatch: (action, ...arg) => dispatch(action, ...arg)
        }
        // 遍历调用一下,闭包 、他们传入未来可能会用到的参数
        const china = middleWare.map(middle => middle(api));

        // 加强dispatch

        //  action => {
        //    if (typeof action === 'function') {
        //     return next(action())
        //    }
        //    return next(action)
        //  }
        // next 是前一个中间件也是这个玩意,
        //  action => {
        //    if (typeof action === 'function') {
        //     return next(action())
        //    }
        //    return next(action)
        //  }
        // 直到最后没有中间件了dispatch真身才真的遇到了action,现在为止,这个函数没调用呢
        // 等到用户dispatch的时候才触发第一个中间件函数
        // (...arg)=>fn3((...arg)=>fn2((...arg)=>fn1(...arg)))====> 恕在下无能,实在无法表达,层层闭包,
        dispatch = compose(...china)(dispatch)

        // 覆盖原来的dispatch
        return {
            ...store,
            dispatch
        }
    }
}
// compose函数
function compose(...china) {
    // 判断长度,避免处理简单的和空的
    if (china.length === 0) {
        // 没有中间件,返回空函数,接收啥就返回啥
        return arg => arg;
    }
    if (china.length === 1) {
        // 就一个就返回这个函数就行了
        // 类似,或者说就是这个functio
        // 就是我要调用next(action)中间做一些事,实际就是dispatch(action)中间
        // next => action => {
        //     // 判断action是否为函数
        //     if (typeof action === 'function') {
        //         return next(action())
        //     }
        //     return next(action)
        // }
        return china[0]
    }
    // 这个和上面一样
    //     next => action => {
    //    
    //        if (typeof action === 'function') {
    //         return next(action())
    //        }
    //        return next(action)
    //      }
    // 每个fn都类似上面这个函数
    // [fn1,fn2,fn3]====>(...arg)=>fn3((...arg)=>fn2((...arg)=>fn1(...arg)))
    return china.reduce((prev, next) => (...arg) => next(prev(...arg)));
}

// 其他数据结构,所以按照自己需要应用中间件
// thunk, logger中间件简单实现
// 中间件能接收一个对象参数,里面有dispatch和getStore
function thunk({ dispatch, getStore }) {
    return next => action => {
        // 判断action是否为函数
        console.log(3333)
        if (typeof action === 'function') {
            return dispatch(action())
        }
        return next(action)
    }
}

function logger({ dispatch, getStore }) {
    return next => action => {
        //    打印pre store 
        console.log(getStore(), '之前的store');
        let returnNext = next(action);
        //   打印next store 
        console.log(getStore(), '改变后的store');
        // 别忘了下一个中间件参数
        return returnNext;
    }
}





// reudecer纯函数
/*params
*store 原来store值 action,描述及payload参数
*/
const currentReducer = (store = { count: 0 }, action) => {
    switch (action.type) {
        case "ADD": return { count: store.count   action.payload };
        case "SUB": return { count: store.count - action.payload };
        default: return store;
    }
}
const ageReducer = (store = { age: 100 }, action) => {
    switch (action.type) {
        case "ADDAGE": return { age: store.age   action.payload };
        case "SUB": return { age: store.age - action.payload };
        default: return store;
    }
}



// 用法
// applyMiddleWare应用中间件
let store = createStore(combineReducers({ currentReducer, ageReducer }), applyMiddleWare(thunk, thunk, logger));

export default store;

0 人点赞