Redux 中间件方式实现 dva功能

博客提及 JavaScript 在线实现地址,包含需求、实现及使用方法等内容,还给出了转载来源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在线的实现地址

需求:

// counter.js
// 拥有 dva model 基本编程规则,不考虑使用错误情况,如 namespace:'home/abc'...
export default {
    namespace: 'counter',
    state: {count:0},
    effects: {
        // 可使用 Generator,传入参数与 dva 基本一致
    	*set({payload, callback}, {call, put,select}) {
            // 实现 put 方法,用法与dva 基本一致,可调用本 model 或 其他  namespace model 的 reducers 方法
            // 实现 select 方法,用法与 dva 基本一致
            // 实现 call 方法,用法与 dva 基本一致,需要支持异步(只考虑 Promise 情况)
             /** 
              支持语法:
              let count =  yield call(count,payload,1000)
              let {
                count,
                someName
              } = yield call({
                count: call(count, payload, 1000),
                someName: call(someFunc, payload)
              })
              let [count,someName] = yield call([
                call(count,payload,1000),
                call(someFunc,payload)
              ])
            */
            const state = yield select((state)=> state)
            const data = yield call(count,payload,1000)
            yield put('setProps',data)
            callback && callback(state)
        }
    },
    reducers: {
        // 与 dva 一致,执行和会触发 redux 中的 dispatch(中间件的 next 方法)
    	setProps(state,action){
    		return {...state,...action.payload}
    	}
    }
}

function count(data,delay = 0){
    return new Promise((resolve,reject)=>{
        return setTimeout(_=>{
            resolve(data)
        },delay)
    })
}

复制代码

实现:

//myDva.js
function isGenerator(obj) {
    return 'function' === typeof obj.next && 'function' === typeof obj.throw
}
function isPromise(obj){
    return obj instanceof Promise || 'function' === typeof obj.then
}
function isObject(value){
    return ({}).toString.call(value) === '[object Object]'
}
// 让 yield 对应为异步方法时(返回 Promise),执行完才执行下一行
// 这里实现了不需要 call 方法也可以执行异步方法 
function promiseQueue(call,promises){
    let type = Array.isArray(promises) ? 'array': isPromise(promises) ? 'promise': isObject(promises) ? 'object': void 0
    // 不支持 Set Map...
    if(!type) return
    if(type === 'promise') promises = [promises]
    let keys = Object.keys(promises)
    promises = Object.values(promises)
    promises && Promise.all(promises).then(
        onFulfilled.bind(this, call, 'reslove', type, keys),
        onFulfilled.bind(this, call, 'reject')
    ).catch(onFulfilled.bind(this, call, 'reject'))
}

// Promise.all 注册的函数,来触发 next 行为
function onFulfilled( call, fulfill, type, keys, res ) {
    if (!isGenerator(call)) return
    /** 默认:
        let [count,someName] = yield call([
            call(count,payload,1000),
            call(someFunc,payload)
        ])
    */
    if(type === 'promise') {
        //实现  let count =  yield call(count,payload,1000)
        res = res && res[0]
    }else if(type === 'object'){
        /**
         * 实现
            const { count,someName} = yield call({
                count: call(count, payload, 1000),
                someName: call(someFunc, payload)
            })
        */
        let rel = {}
        keys.forEach((k,i)=> rel[k] = res[i])
        res = rel
    }
    let next = {}
    if (fulfill === 'reslove') {
        next = call.next(res)
    } else {
        // Promise.all reject 时
        next = call.throw(res)
    }
    if (!next.done) {
        taskGenerator(call,next.value)
    }
}

/**
 * 对嵌套的 Generator 与 Promise 可以正常支持
 * @param {*} call Generator 函数
 * @param {*} value 
 */
function taskGenerator(call,value) {
    if (isGenerator(call)) {
        let isEffect = null
        let next = {}
        if(value) next.value = value
        while (!isEffect) {
            if (isGenerator(next)) {
                taskGenerator(next.value)
            } else if (next.value && (isPromise(next.value) || typeof next.value === 'object')) {
                // 可能有 Promise 时
                isEffect = null
                promiseQueue(call, next.value)
                break
            }else{
                // 执行 Generator 对应 yield 的表达式或函数
                // 将上一次的 值放入 
                next = call.next(next.value)
            }
            // next.done = true 停止执行 next
            isEffect = next.done
        }
    }
}

function dva() {
    // dvaModels 存储 model
    let dvaModels = {}
    /**
     * 返回 { namespace: function (){//Reducer 方法} }
     * @param {Array} models 
     */
    function createReducers(models) {
        models.forEach(model => {
            // 已 model.namespace 为 key 的形式赋予 dvaModels 中
            dvaModels[model.namespace] = model
        })
        let reducerKeys = Object.keys(dvaModels)
        let reducer = {}
         // 生成 redux 的 combineReducers 使用的 reducer
        reducerKeys.forEach(key => {
            reducer[key] = (state, action, _key = key) => {
                let thatAtcion = dvaModels[_key]
                state = state || thatAtcion.state
                if (action.type === _key) {
                    return action.data
                }
                return state
            }
        })
        return reducer
    }
    /**
     * 触发 model对应 type 的 reducers 方法,并触发 中间件的 next(dispatch) 方法
     *  put('setProps':type,data:payload)
     * @param {*} target 
     * @param {*} next 
     * @param {*} type 
     * @param {*} payload 
     */
    function _put(target, next, type, payload) {
        let args = type.split('/')
        let reducerKey = args[0]
        if (args.length === 2) {
            target = args[0]
            reducerKey = args[1]
        }
        let reducer = dvaModels[target].reducers[reducerKey]
        if (typeof reducer === 'function') {
            let state = dvaModels[target].state
            dvaModels[target].state = reducer(state, {type,payload})
            next({
                type: target,
                data: dvaModels[target].state
            })
        }
    }
    /**
     *  可以触发异步函数,其实已经是实现了不用 call 也可以使用异步
     *  yield call(delay:fnDescriptor,arg1,arg2)
     * @param {*} fnDescriptor 
     * @param  {...any} args 
     */
    function _call(fnDescriptor, ...args) {
        if(typeof fnDescriptor === 'function'){
            return fnDescriptor(...args)
        }else if(fnDescriptor){
            return fnDescriptor
        }
    }
    /**
     *  callback 参数为所有 model 中的 state
     * const state = yield select((state)=> state)
     * @param {*} callback 
     */
    function _select(callback){
        if( typeof callback !== 'function') return
        let reducerKeys = Object.keys(dvaModels)
        let state ={}
        reducerKeys.forEach(item =>{
            state[item] = dvaModels[item].state
        })
       return callback(state)
    }
    /**
     * store.dispatch({
     *  type:'counter/set',
     *  payload:{counter:++counter}
     *  callback:()=>{}
     * })
     * @param {*} param0 
     * @param {*} next 
     */
    function _dispatch({type = '',payload = {},callback}, next) {
        let args = type.split('/')
        let target = dvaModels[args[0]]
        let effect = target.effects[args[1]]
        let Effect = effect && effect({payload,callback},{
            // args[0] = namespace
            // next = store.dispatch
            put: _put.bind(this, args[0], next),
            call: _call,
            select:_select
        })
        // 异步方法(使用Promise)在利用 Generator 函数实现同步(串行)执行
        Effect && taskGenerator(Effect)
    }

    const middleware = store => next => action => {
        _dispatch(action, next)
    }
    return {
        // 将 model 转成 redux reducer 形式
        createReducers,
        // dva 中间件
        middleware
    }
}
export default dva()
复制代码

使用方法:

import React, { Component } from 'react';
import {createStore, combineReducers,applyMiddleware} from 'redux'
import {Provider} from './react-redux'
import myDva from './myDva'
import counter from './counter'

// myDva.createReducers([counter]) 以数组的方式注册 model
const CombineReducers = combineReducers(myDva.createReducers([counter]))
// 注册 myDva 中间件
const middleware = applyMiddleware(myDva.middleware)
const store = createStore(CombineReducers,middleware)

/**
使用:
App.js
// ...
store.dispatch({
    type:'counter/set',
    payload:{counter:++counter}
    callback:(state)=>{}
})
// ...
*/

class App extends Component {
  render() {
    return (
        <Provider store={store} >
            <App />
        </Provider>
    );
  }
}

复制代码

转载于:https://juejin.im/post/5ce64c23f265da1ba647c50a

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值