rematch常用插件介绍

rematch常用插件介绍插件系统 rematch 实现了一个插件系统 内置了 dispatch 和 effects 两个插件 分别用来增强 dispatch 和处理异步操作 rematch 的插件 要符合 rematch 的要求 每个插件返回一个对象 这个对象可以包含几个属性 用来在不同的生命周期中对 store 进行操作 对于每一个插件对象 提供了如下几个属性进行配置 onInit 进行插件的初始化工作 config 对 rematch 进行配置

插件系统

rematch实现了一个插件系统,内置了dispatch和effects两个插件,分别用来增强dispatch和处理异步操作。rematch的插件,要符合rematch的要求,每个插件返回一个对象,这个对象可以包含几个属性,用来在不同的生命周期中对store进行操作。

对于每一个插件对象,提供了如下几个属性进行配置。

  • onInit:进行插件的初始化工作
  • config: 对rematch进行配置,会在rematch初始化之前,对插件的config配置进行merge操作
  • exposed:暴露给全局的属性和方法,可以理解为占位符,用于数据的共享
  • middleware:相当于redux的中间键,如果提供了这个属性,会把它交给redux进行处理
  • onModel:加载model的时候会调用的方法,用来对model进行处理
  • onStoreCreated:当rematch的store对象生成后,调用的方法,生成最终的store对象

DispatchPlugin

plugin包含exposed、onStoreCreated和onModel三个属性

const dispatchPlugin: R.Plugin = { exposed: { storeDispatch(action: R.Action, state: any) { console.warn('Warning: store not yet loaded') }, storeGetState() { console.warn('Warning: store not yet loaded') }, dispatch(action: R.Action) { return this.storeDispatch(action) }, createDispatcher(modelName: string, reducerName: string) { return async (payload?: any, meta?: any): Promise 
   
     => { const action: R.Action = { 
    type: ` 
    ${modelName}/ 
    ${reducerName}` } 
    if (typeof payload !== 
    'undefined') { action.payload = payload } 
    if (typeof meta !== 
    'undefined') { action.meta = meta } 
    return this.dispatch(action) } }, }, // 在store创建完成的时候调用,将增强后的dispatch方法抛出 onStoreCreated(store: any) { this.storeDispatch = store.dispatch this.storeGetState = store.getState 
    return { dispatch: this.dispatch } }, // 对model上的每个reducer创建action createor,并挂载到dispatch对象上 onModel(model: R.Model) { this.dispatch[model.name] = {} 
    if (!model.reducers) { 
    return } 
    for (const reducerName of Object.keys(model.reducers)) { this.validate([ [ !!reducerName.match(/\/.+\//), `Invalid reducer name ( 
    ${model.name}/ 
    ${reducerName})`, ], [ typeof model.reducers[reducerName] !== 
    'function', `Invalid reducer ( 
    ${model.name}/ 
    ${reducerName}). Must be a 
    function`, ], ]) this.dispatch[model.name][reducerName] = this.createDispatcher.apply( this, [model.name, reducerName] ) } }, } 
    复制代码 
   

这个插件用来处理model的reducers属性,如果model没有reducers,就直接退出;否则,遍历reducers,如果键名包含是/符号开头结尾的或者值不是一个函数,就报错退出。

通过createDispatcher函数,对每个reducer进行处理,包装成一个异步的action creator,action的type由model.name和reducerName组成。

onStoreCreated属性,会返回增强后的dispatch方法去重置redux默认的dispatch方法。

EffectsPlugin

effects plugin包含exposed、onModel和middleware三个属性。

const effectsPlugin: R.Plugin = { exposed: { effects: {}, }, // 将每个model上的effects添加到dispatch上,这样可以通过dispatch[modelName][effectName]来调用effect方法 onModel(model: R.Model): void { if (!model.effects) { return } // model的effects可以是一个对象,或者是一个返回对象的函数,这个函数的参数是全局的dispatch方法 const effects = typeof model.effects === 'function' ? model.effects(this.dispatch) : model.effects for (const effectName of Object.keys(effects)) { this.validate([ [ !!effectName.match(/\//), `Invalid effect name (${model.name}/${effectName})`, ], [ typeof effects[effectName] !== 'function', `Invalid effect (${model.name}/${effectName}). Must be a function`, ], ]) this.effects[`${model.name}/${effectName}`] = effects[effectName].bind( this.dispatch[model.name] ) this.dispatch[model.name][effectName] = this.createDispatcher.apply( this, [model.name, effectName] ) // isEffect用来区分是普通的action,还是异步的,后面的loading插件就是通过这个字段来判断是不是异步操作 this.dispatch[model.name][effectName].isEffect = true } }, // 用来处理 async/await actions的redux中间键 middleware(store) { return next => async (action: R.Action) => { if (action.type in this.effects) { await next(action) // 会把全局的state作为effect方法的第二个参数传入 return this.effects[action.type]( action.payload, store.getState(), action.meta ) } return next(action) } }, } 复制代码

通过exposed将effects挂载到rematch对象上,用来保存所有model中的effects方法。

middleware属性用来定义中间键,处理async/await 异步函数。

onModel用来将所有model的effects方法存储在dispatch上,并使用’modelName/effectName’的key将对应的effect方法存储在全局的effects对象上。

@rematch/loading

对每个model中的effects自动添加loading状态。

export default (config: LoadingConfig = {}): Plugin => { validateConfig(config) const loadingModelName = config.name || 'loading' const converter = config.asNumber === true ? (cnt: number) => cnt : (cnt: number) => cnt > 0 const loading: Model = { name: loadingModelName, reducers: { hide: createLoadingAction(converter, -1), show: createLoadingAction(converter, 1), }, state: { ...cntState, }, } cntState.global = 0 loading.state.global = converter(cntState.global) return { config: { // 增加一个loading model,用来管理所有的loading状态 models: { loading, }, }, onModel({ name }: Model) { // 用户定义的model如果和注入的loadingmodel重名,则退出这个model if (name === loadingModelName) { return } cntState.models[name] = 0 loading.state.models[name] = converter(cntState.models[name]) loading.state.effects[name] = {} const modelActions = this.dispatch[name] // 收集每个model上的effect方法 Object.keys(modelActions).forEach((action: string) => { // 通过isEffect来判断是否是异步方法 if (this.dispatch[name][action].isEffect !== true) { return } cntState.effects[name][action] = 0 loading.state.effects[name][action] = converter( cntState.effects[name][action] ) const actionType = `${name}/${action}` // 忽略不在白名单中的action if (config.whitelist && !config.whitelist.includes(actionType)) { return } // 忽略在黑名单中的action if (config.blacklist && config.blacklist.includes(actionType)) { return } // 指向原来的effect方法 const origEffect = this.dispatch[name][action] // 对每个effect方法进行包裹,在异步方法调用前后进行状态的处理 const effectWrapper = async (...props) => { try { // 异步方法请求前,同步状态 this.dispatch.loading.show({ name, action }) const effectResult = await origEffect(...props) // 异步请求成功后,同步状态 this.dispatch.loading.hide({ name, action }) return effectResult } catch (error) { // 异步请求失败后,同步状态 this.dispatch.loading.hide({ name, action }) throw error } } // 使用包裹后的函数替代原来的effect方法 this.dispatch[name][action] = effectWrapper }) }, } }复制代码

这个插件会在store上增加一个名为loading的model,这个model的state有三个属性,分别为global、models和effects。

  • global:用来保存全局的状态,只要任意effect被触发,就会引起global的更新
  • models:它是一个对象,用来表示每个model的状态,只要是这个model下的effect被触发,就会引起对应model字段的更新
  • effects:它是一个对象,以model为单位,记录这个model下每个effects的状态,如果某个effect被触发,会去精确的更新这个effect对应的状态。

在默认情况下,会遍历所有model的effects,对每个isEffect为true的action,用一个异步函数进行包裹,使用try catch捕获错误。

常用的配置项

  • asNumber:默认是false,如果设置为true,那么状态就是异步被调用的次数
  • name:loading model的name,默认是loading
  • whitelist:白名单,一个 action 列表,只收集在列表中的action的状态。命名使用“model名称” / “action名称”,{ whitelist: ['count/addOne'] })
  • blacklist:黑名单一个 action 列表,不使用 loading 指示器。{ blacklist: ['count/addOne'] })
store.js import { init } from '@rematch/core' import createLoadingPlugin from '@rematch/loading' // 初始化配置 const loading = createLoadingPlugin({}) init({ plugins: [loading] }) 复制代码
exmaple modle.js const asyncDelay = ms => new Promise(r => setTimeout(r, ms)); export default { state: 0, reducers: { addOne(s) { return s + 1 } }, effects: { async submit() { // mocking the delay of an effect await asyncDelay(3000) this.addOne() }, } } 复制代码
app.js const LoginButton = (props) => ( 
   
     Login 
    ) const mapState = state => ({ count: state.example, loading: { global: state.loading.global, // true when ANY effect is running model: state.loading.models.example, // true when ANY effect on the `login` model is running effect: state.loading.effects.example.submit, // true when the `login/submit` effect is running }, }) const mapDispatch = (dispatch) => ({ submit: () => dispatch.login.submit() }) export default connect(mapState, mapDispatch)(LoginButton) 复制代码

@rematch/immer

immer 是Mobx作者写的一个immutable库,利用ES6的proxy和defineProperty实现js的不可变数据。相比于ImmutableJS,操作更简单,也不用学习特有的API。

对于一个复杂的对象,immer会复用没有改变的部分,仅仅替换修改了的部分,相比于深拷贝,可以大大的减少开销。

对于react和redux,immer可以大大减少setState和reducer的代码量,并提供更好的可读性。

举个栗子,想要修改todoList中某项的状态,常规的写法

todos = [ {todo: 'bbbb', done: true}, {todo: 'aaa', done: false} ]; state的情况 this.setState({ todos: [ ...todos.slice(0, index), { ...todos[index], done: !todos[index].done }, ...todos.slice(index + 1) ] }) reducer的情况 const reducer = (state, action) => { switch (action.type) { case 'TRGGIER_TODO': const { members } = state; return { ...state, todos: [ ...todos.slice(0, index), { ...todos[index], done: !todos[index].done }, ...todos.slice(index + 1) ] } default: return state } } 复制代码

如果使用immer,代码可以简化为

import produce from 'immer' state的情况 this.setState(produce(draft => { draft.todos[index].done = !draft.todos[index].done; )) reducer的情况 const reducer = produce((draft, action) => { switch (action.type) { case 'TRGGIER_TODO': draft.todos[index].done = !draft.todos[index].done; } }) 复制代码

这个插件重写了redux的combineReducers方法,从而支持了immer。

import { Models, Plugin } from '@rematch/core' import produce from 'immer' import { combineReducers, ReducersMapObject } from 'redux' function combineReducersWithImmer(reducers: ReducersMapObject) { const reducersWithImmer = {} // model的reducer函数必须有返回,因为immer只支持对象类型 for (const [key, reducerFn] of Object.entries(reducers)) { reducersWithImmer[key] = (state, payload) => { // 如果state不是对象,则直接返回reducer的计算值 if (typeof state === 'object') { return produce(state, (draft: Models) => { const next = reducerFn(draft, payload) if (typeof next === 'object') { return next } }) } else { return reducerFn(state, payload) } } } return combineReducers(reducersWithImmer) } const immerPlugin = (): Plugin => ({ config: { redux: { combineReducers: combineReducersWithImmer, }, }, }) export default immerPlugin 复制代码

使用插件

store.js import { init } from '@rematch/core' import immerPlugin from '@rematch/immer'; const immer = immerPlugin(); init({ plugins: [immer] }) 复制代码
model.js const immerTodos = { state: [{ todo: 'Learn typescript', done: true, }, { todo: 'Try immer', done: false, }], reducers: { addTodo(state, payload) { state.push(payload); return state; }, triggerTodo(state, payload) { const { index } = payload; state[index].done = !state[index].done; return state; }, }, }; export default immerTodos; 复制代码

rematch-plugin-default-reducer

对于一般的model而言,reducer的作用就是根据state和action来生成新的state,类似于(state, action) => state,这个plugin的作用是为每个model生成一个名为setValues的reducer。如果是state是一个对象,会对action.payload和state使用Object.assign去进行属性的合并;如果state是基础类型,会使用action.payload去覆盖state。

const DefaultReducerPlugin = { onModel(model) { const { reducers = {} } = model; if (Object.keys(reducers).includes('setValues')) { return false; } reducers.setValues = (state, payload) => { if (isObject(payload) && isObject(state)) { return Object.assign({}, state, payload); } return payload; }; this.dispatch[model.name].setValues = this.createDispatcher.apply( this, [model.name, 'setValues'], ); }, }; 复制代码

使用

const countModel = { state: { configList: [], }, effects: { async fetchConfigList() { const res = await fetch({url}); res && res.list && this.setValues({ configList: res.list, }); }, }, }; init import defaultReducerPlugin from 'rematch-plugin-default-reducer'; const store = init({ ... models, plugins: [defaultReducerPlugin], ... });复制代码
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/201884.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月20日 上午8:23
下一篇 2026年3月20日 上午8:23


相关推荐

  • intellijidea2021最新激活码_最新在线免费激活

    (intellijidea2021最新激活码)2021最新分享一个能用的的激活码出来,希望能帮到需要激活的朋友。目前这个是能用的,但是用的人多了之后也会失效,会不定时更新的,大家持续关注此网站~IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.net/100143.html…

    2022年3月26日
    64
  • 大数据管理与应用专业总结笔记

    大数据管理与应用专业总结笔记大数据管理与应用专业:数据科学教育特点:不仅依赖于传统的信息管理于信息系统专业,更依赖于计算机、数学、统计等学科。大数据专业十一门涉及广泛的交叉性的学科。大数据时代的下的理念(维克托·迈尔·舍恩伯格):一是更相关性而不是因果性;二是更关注数据的纷繁复杂,而不是数据的精准;三是全部数据,而不是抽样数据。维克托·迈尔·舍恩伯格:维克托·迈尔-舍恩伯格是十余年潜心研究数据科学的技术权威,是最早洞见大数据时代发展趋势的数据科学家之一,也是最受人尊敬的权威发言人之一。**目前的形势:**目前国内新增院校还不多

    2022年6月10日
    42
  • BERT中的词向量指南,非常的全面,非常的干货[通俗易懂]

    BERT中的词向量指南,非常的全面,非常的干货[通俗易懂]点击上方“AI公园”,关注公众号,选择加“星标“或“置顶”作者:ChrisMcCormick编译:ronghuaiyang导读在本文中,我将深入研究谷歌的BERT生成的…

    2022年6月10日
    37
  • vs 注释快捷键[通俗易懂]

    vs 注释快捷键[通俗易懂]选中要注释的代码,然后Ctrl+k+c注释取消注释:Ctrl+k+u

    2022年8月15日
    12
  • 为Eclipse指定Java VM[通俗易懂]

    重装系统后,Eclipse无法启动,说找不到JavaVM.   网上搜了一下,解决方法如下:   创建快捷方式,右键快捷方式,打开属性.在目标处输入” -vm你的Java根目录\jre\bin\javaw.exe”.然后用快捷方式打开就可以了.

    2022年4月12日
    106
  • HTML5新增了哪些标签_标签标准

    HTML5新增了哪些标签_标签标准在我们平时所写的ASP.NET中,我们更多的是去使用服务器端控件:<asp:>。与此同时,我们却忽略了很多HTML元素的使用。在这章,让我们来简单的对HTML的一些元素的标准来做简单的回顾。1.<q>和<blockquote>对于这两个元素,我想很多经常去使用.NET服务器端控件,以及VisualStudio或者DW等可视化工具的人,应该对他…

    2022年10月17日
    4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号