優雅的redux異步中間件 redux-effect

不吹不黑,redux蠻好用。只是有時略顯繁瑣,叫我定義每個action、action type、使用時還要在組件上綁定一遍,臣妾作不到呀!下面分享一種我的比較傾向的極簡寫法,仍有待完善,望討論。javascript

github: github.com/liumin1128/…java

基於redux、async/await、無侵入、兼容性良好的異步狀態管理器。git

install

npm i -S redux-effect

// or
yarn add redux-effect
複製代碼

use

import { createStore, applyMiddleware, combineReducers } from 'redux';
import { reduxReduers, reduxEffects } from 'redux-effect';

const models = [ test1, test2, ...];

const reducers = combineReducers(reduxReduers(models));
const middlewares = [ reduxEffects(models) ];

const store = createStore(
    reducers,
    initialState,
    middlewares
  );
複製代碼

從代碼能夠看出,從reduxReduers, reduxEffects中獲得的就是標準的reducer和middleware,完美兼容其餘redux插件,也能夠輕鬆整合進老項目中。github

完整例子:examplenpm

model

在redux-effect中,沒有action的概念,也不須要定義action type。redux

全部關於某個state的一切聲明在一個model中,本質就是一個對象。bash

export default {
  namespace: 'test',
  state: { text: 'hi!' },
  reducers: {
    save: (state, { payload }) => ({ ...state, ...payload }),
    clear: () => ({})
  },
  effects: {
    fetch: async ({ getState,dispatch }, { payload }) => {
      await sleep(3000);
      await dispatch({ type: 'test/clear' });
      await sleep(3000);
      await dispatch({ type: 'test/save', payload: { text: 'hello world' } });
    }
  }
};

複製代碼

namespace:app

model的命名空間,對應state的名字,必填,只接受一個字符串。異步

state:async

state的初始值,非必填,默認爲空對象

reducers:

必填,至關於同步執行的action方法,接受兩個參數state和action,合併後返回新的state狀態值。

effects:

非必填,至關於異步執行的action方法,接受兩個參數store和action,store裏包括redux自帶的getState和dispatch方法,action爲用戶dispatch時帶的參數。

dispatch

這裏的dispatch就是redux中的dispatch,但有幾個約定。

  1. 不傳定義好的action,而是直接傳一個普通對象。
  2. type的組織形式:namespace + '/' + reducer或effect方法名
  3. 參數的傳遞:須要合併的參數用payload包裹

定義每個action,並將其綁定到視圖層過於繁瑣,去action化則讓事件的觸發變的靈活。

普通事件

發送事件時,不區分同步仍是異步,只管dispatch,一切都已在model中定義好。

// 同步
dispatch({ type: 'test/save', payload: { text: "hello world" } })
// 異步
dispatch({ type: 'test/fetch' })
複製代碼

等待

等待一個事件完成再執行邏輯,dispatch方法是能夠被await的,十分輕鬆。

async function test() {
  await dispatch({ type: 'test/fetch' })
  await console.log('hello world')
}
複製代碼

回調

等待某個事件,再執行外部定義的某個回調函數,只須要在action字段里加上callback方法,在effect中調用便可。

相比較await,回調能夠拿到某些返回值,也能夠在effect流程的中間部分執行。

dispatch({ type: 'test/fetch', callback1, callback2 })

{
  effects: {
    fetch: async ({ getState,dispatch }, { payload, callback, callback2 }) => {
      const state = await getState()
      await sleep(3000);
      await callback1(state)
      await sleep(3000);
      await callback2(state)
    }
  }
}
複製代碼

自定義reducer

reducer其實就是redux中的reducer,用法徹底同樣。好比定義一個push方法,將後續數據,壓入到原有數據後面,能夠這樣寫。

export default {
  namespace: 'test',
  state: { data: [] },
  reducers: {
    save: (state, { payload }) => ({ ...state, ...payload }),
    clear: () => ({}),
    push: (state, { payload = {} }) => {
      const { key = 'data', data } = payload;
      return { ...state, [key]: state[key].concat(data) };
    }
  },
};
複製代碼

自定義effect

effect其實就是一個普通async函數,接受store和action兩個參數,可使用async/await,能夠執行任意異步方法,能夠隨時拿到state的值,能夠dispatch觸發另外一個effect或者reducer。

loading

也許你會想監聽某個effect,拿到loading狀態,以便在ui給用戶一個反饋。通常狀況下監聽一個異步方法,只須要在effect的開頭和結束,各自設定狀態便可,與常規寫法無異。

但這裏也提供一種model級別的loading狀態,新增一個名爲loading的model,再使用reduxEffectsWithLoading包裹須要監聽的model便可。

關於model-creator

以上所作的事情,是將redux核心規範爲model,獲得了統一且能夠複用的數據模型,這爲自動生成model創造了可能性,若是能經過工廠模式,自動化建立具備相似功能,且能夠隨意裝配的model,一切將變得更加美好。

Coming Soon

相關文章
相關標籤/搜索