【dva】dva使用与实现(七)

13 篇文章 0 订阅
7 篇文章 2 订阅

前言

  • 本篇通过学习dva-immer来实现_handleAction钩子和onError钩子

dva-immer

  • 先安装看用法:
cnpm i dva-immer -S
  • 顺便说一下,dva目前npmjs下的是2.41,2.5和2.6都是beta版本,去发布网址查看。dva-immer版本没对上的话安装可能提示需要的dva版本不对。不过没关系,这个不影响。
  • 我们在reducer里常常会这样修改状态:
  state:{product:[{number:1},{commit:[{id:1},{id:2}]}],number2:4}
  reducers: {
        add(state,action) {
            return {...state,product:[{number:1},{commit:[{id:1},{id:3}]}]}
        }
    },
  • 是不是非常麻烦。我如果就改最后那个,那得还把原来的给弄出来。有了immer就可以方便的修改了。
  • 可以直接state.product[1].commit[1].id=3就行了。比如:
import {produce}from 'immer'
...
  state:{product:[{number:1},{commit:[{id:1},{id:2}]}],number2:4}
  reducers: {
        add(state,action) {
            return produce((state,draftState=>{
            	draftState.product[1].commit[1].id=3
            }))
        }
    },
  • 但这样写还是麻烦,每次都要写produce,如果想直接在里面写修改那句,就需要用dva-immer了。
app.use(require('dva-immer').default())
....
    reducers: {
        add(state, action) {
            state.number += 1
        },
  • 这就可以了,直接把state改掉,相当于在操作draftState。
  • 注意列表添加之类需要push或者shift之类操作方法。
  • 下面就来实现dva-immer,实际用的是_handleActions这个钩子,打印下就看见了。

dva-immer.js

import produce from 'immer'

export default function () {
    return {
        _handleActions(reducers, defaultState) {
            return function (state = defaultState, action) {
                let { type } = action
                let ret = produce(state, draft => {
                    const reducer = reducers[type]
                    if (reducer) {
                        return reducer(draft, action)
                    }
                })
                return ret || {}
            }
        }
    }
}
  • 这个钩子有点像用户配置的中间件,其中reducer(draft,action)可以看出,传给model的reducers里的其实是draft,用户在reducers里写函数实际写的是reducer(draft, action)。
  • 下面在我们的dva中加入它。首先是plugin里加入:
const hooks = [
    "onEffect",//增强effect
    "extraReducers",//添加reducer
    "onAction",
    "onStateChange",
    "onReducer",
    "extraEnhancers",
    "_handleActions"
]
...
    use(plugin) {
        const { hooks } = this
        for (let key in plugin) {
            if (key === 'extraEnhancers') {
                hooks[key] = plugin[key]
            } else if (key === '_handleActions') {
                this._handleActions = plugin[key]
            } else {
                hooks[key].push(plugin[key])
            }
        }//{hook:[fn|obj]}
    }
  • 这里赋值的话是直接给这个实例上属性。这样实例可以通过plugin._handleActions拿到。
  • 还记得当时写dva的index.js里有个getReducer函数,里面当action派发过来会查找对应的reducer,匹配后执行用户逻辑吗?没错,dva的index.js里就把这个钩子加在这里:
    function getReducer(m) {
        let handleActions = plugin._handleActions
        let everyreducers = m.reducers//reducers的配置对象,里面是函数
        let reducer = function (state = m.state, action) {//组织每个模块的reducer
            let reducer = everyreducers[action.type]//相当于以前写的switch
            if (reducer) {
                return reducer(state, action)
            }
            return state
        }
        if (handleActions) {
            return handleActions(everyreducers, m.state)
        } else {
            return reducer
        }
    }
  • 这样这个钩子就完成了。

onError

  • 这个钩子是全局捕获错误的,这个会捕获用户传入effects的逻辑错误和subscription的done错误
...
     onError(e) {
        alert(e)
    }
...
 subscriptions: {
        subscribe({ dispatch, history }, done) {
            //这里可以输入一些监听事件,满足监听条件进行dispatch或者别的操作。
            //就相当于不需要手动派发,而是监听派发
            done("错误")
            history.listen((location) => {
                console.log(location.pathname);
                document.title = location.pathname
                //这里操作dom会有顺序问题,这里比react渲染dom先调用,所以react的渲染会覆盖这里操作。
            })
        }
    }
  • 实现的话一样,加入钩子:
const hooks = [
    "onEffect",//增强effect
    "extraReducers",//添加reducer
    "onAction",
    "onStateChange",
    "onReducer",
    "extraEnhancers",
    "_handleActions",
    "onError"
]
  • 然后需要在effects和subscription里添加error的回调:
function getWatcher(key, effect, model, onEffect, onError) {
    function put(action) {
        return sagaEffects.put({ ...action, type: prefixType(action.type, model) })
    }
    return function* () {
        yield sagaEffects.takeEvery(key, function* (action) {//对action进行监控,调用下面这个saga
            if (onEffect) {
                for (const fn of onEffect) {//oneffect是数组
                    effect = fn(effect, { ...sagaEffects, put }, model, key)//这里只是存起来,多个effect就递归
                }
            }
            try {
                yield effect(action, { ...sagaEffects, put })//真正的用户effect执行逻辑
            } catch (e) {
                onError.forEach(fn => fn(e))//报错了传给它就行
            }

        })
    }
}
    function runSubscription(m) {
        for (let key in m.subscriptions) {
            let subscription = m.subscriptions[key]
            subscription({ history, dispatch: app._store.dispatch }, error => {//加个参数就行了,这里就是done
                let onError = plugin.get('onError')
                onError.forEach(fn => fn(error))
            })
        }
    }
  • 这样就完成了。试试在effects里throw个错误或者subscriptions里调用done走不走onError。
  • dva内容差不多就这么多了。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,下面是使用dva配置自增计数器的步骤: 1. 首先,在src/models目录下创建一个名为counter.js的文件。 2. 在counter.js中导入dva和antd库,并定义一个名为counter的模型对象。 ``` import { message } from 'antd'; import { delay } from 'roadhog-api-doc'; import { addCount } from '@/services/api'; export default { namespace: 'counter', state: { count: 0, }, reducers: { add(state) { return { ...state, count: state.count + 1 }; }, }, effects: { *addAsync(_, { call, put }) { yield call(delay, 1000); const response = yield call(addCount); if (response.success) { yield put({ type: 'add' }); } else { message.error(response.msg); } }, }, }; ``` 3. 在src/services目录下创建一个名为api.js的文件,并在其中定义一个名为addCount的函数用于模拟数据请求。 ``` import { delay } from 'roadhog-api-doc'; export async function addCount() { await delay(1000); return { success: true }; } ``` 4. 在src/routes目录下创建一个名为Counter.js的文件,并在其中定义一个名为Counter的组件。 ``` import React from 'react'; import { connect } from 'dva'; import { Button } from 'antd'; const Counter = ({ dispatch, count }) => { const handleClick = () => { dispatch({ type: 'counter/addAsync' }); }; return ( <div> <h1>当前计数:{count}</h1> <Button type="primary" onClick={handleClick}> 自增 </Button> </div> ); }; export default connect(({ counter }) => ({ count: counter.count }))(Counter); ``` 5. 最后,在src/index.js中导入dva并注册counter模型对象。 ``` import dva from 'dva'; import counter from './models/counter'; import Router from './router'; const app = dva(); app.model(counter); app.router(Router); app.start('#root'); ``` 这样就完成了使用dva配置自增计数器的过程,你可以在Counter组件中点击按钮进行自增操作,同时在控制台中查看redux状态的变化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

业火之理

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值