前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >redux源码简单实现

redux源码简单实现

作者头像
刘嘿哈
发布2022-10-25 13:59:25
3770
发布2022-10-25 13:59:25
举报
文章被收录于专栏:js笔记

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;
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-08-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • redux核心方法createStore
    • 帮助方法
      • 中间件
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档