精讀《從新思索 Redux》

本周精讀內容是 《從新思索 Redux》

1 弁言

《從新思索 Redux》是 rematch 作者 Shawn McKay 寫的一篇乾貨軟文。

dva 以後,有很多基於 redux 的狀況治理框架,但大部份都很範圍,以至是倒退。但直到看到了 rematch,總算以為 redux 社區又進了一步。

這篇文章的珍貴的處所在於,拋開 Mobx、RXjs 觀點,僅針對 redux 做深切的從新思索,對大部份還在運用 redux 的工程場景異常有協助。

2 概述

比較新鮮的是,作者給出一個公式,評價一個框架或東西的質量:

東西質量 = 東西節約的時候/運用東西斲喪的時候

假如如許評價原生的 redux,我們會發明,運用 redux 須要分外消費的時候能夠超過了其節約下來的時候,從這個角度看,redux 是會下降工作效力的。

但 redux 的數據治理頭腦是準確的,龐雜的前端項目也確切須要這類理念,為了更有效力的運用 redux,我們須要運用基於 redux 的框架。作者從 6 個角度論述了基於 redux 的框架須要處置懲罰什麼題目。

簡化初始化

redux 初始化代碼觸及的觀點比較多,比方 compose thunk 等等,同時將 reducerinitialStatemiddlewares 這三個主要觀點拆分成了函數體式格局挪用,而不是更輕易接受的設置體式格局:

const store = preloadedState => {
  return createStore(
    rootReducer,
    preloadedState,
    compose(applyMiddleware(thunk, api), DevTools.instrument())
  );
};

假如換成設置體式格局,明白本錢會下降不少:

const store = new Redux.Store({
  instialState: {},
  reducers: { count },
  middlewares: [api, devTools]
});

筆者注:redux 的初始化體式格局異常函數式,而下面的設置體式格局就更面向對象一些。比擬之下,照樣面向對象的體式格局更好明白,畢竟 store 是一個對象。
instialState 也存在一樣題目,比擬顯現說明,將
preloadedState 作為函數入參就比較籠統了,同時 redux 對初始 state 的賦值也比較隱藏,
createStore 時一致賦值比較彆扭,由於 reducers 是疏散的,假如在 reducers 中賦值,要應用 es 的默許參數特徵,看起來更像營業思索,而不是 redux 供應的才能。

簡化 Reducers

redux 的 reducer 粒度太大,不只致使函數內手動婚配 type,還帶來了 typepayload 等明白本錢:

const countReducer = (state, action) => {
  switch (action.type) {
    case INCREMENT:
      return state + action.payload;
    case DECREMENT:
      return state - action.payload;
    default:
      return state;
  }
};

假如用設置的體式格局設置 reducers,就像定義一個對象一樣,會更清晰:

const countReducer = {
  INCREMENT: (state, action) => state + action.payload,
  DECREMENT: (state, action) => state - action.payload
};

支撐 async/await

redux 支撐動態數據照樣挺費力的,須要明白高階函數,明白中間件的運用體式格局,不然你不會曉得為何如許寫是對的:

const incrementAsync = count => async dispatch => {
  await delay();
  dispatch(increment(count));
};

為何不抹掉明白本錢,直接許可 async 範例的 action 呢?

const incrementAsync = async count => {
  await delay();
  dispatch(increment(count));
};

筆者注:我們發明 rematch 的體式格局,dispatch 是 import 進來的(全局變量),而 redux 的 dispatch 是注入進來的,乍一看好像 redux 更合理,但實在我更推重 rematch 的計劃。經由長期實踐,組件最好不要運用數據流,項目的數據流只用一個實例完全夠用了,全局 dispatch 的設想實在更合理,而注入 dispatch 的設想看似尋求手藝極致,但疏忽了營業運用場景,致使弄巧成拙,增加了不必要的貧苦。

將 action + reducer 改成兩種 action

redux 籠統的 action 與 reducer 的詰問詰責很清晰,action 擔任改 store 之外所有事,而 reducer 擔任改 store,偶然用來做數據處置懲罰。這類觀點實在比較隱約,由於每每不清晰數據處置懲罰放在 action 照樣 reducer 里,同時過於簡樸的 reducer 又要寫 action 與之婚配,覺得過於形式化,而且煩瑣。

從新考慮這個題目,我們只要兩類 action:reducer actioneffect action

  • reducer action:轉變 store。
  • effect action:處置懲罰異步場景,能挪用其他 action,不能修正 store。

同步的場景,一個 reducer 函數就能夠處置懲罰,只要異步場景須要 effect action 處置懲罰掉異步部份,同步部份依舊交給 reducer 函數,這兩種 action 職責更清晰。

不再顯現說明 action type

不要在用一個文件存儲 Action 範例了,const ACTION_ONE = 'ACTION_ONE' 實在反覆寫了一遍字符串,直接用對象的 key 示意 action 的值,再加上 store 的 name 為前綴保證唯一性即可。

同時 redux 發起運用 payload key 來傳值,那為何不強迫運用 payload 作為入參,而要經由過程 action.payload 取值呢?直接運用 payload 不只視覺上削減代碼數目,輕易明白,同時也強迫束縛了代碼作風,讓發起真正落地。

Reducer 直接作為 ActionCreator

redux 挪用 action 比較煩瑣,運用 dispatch 或許將 reducer 經由 ActionCreator 函數包裝。為何不直接給 reducer 自動包裝 ActionCreator 呢?削減榜樣代碼,讓每一行代碼都有營業寄義。

末了作者給出了一個 rematch 完全的例子:

import { init, dispatch } from "@rematch/core";
import delay from "./makeMeWait";

const count = {
  state: 0,
  reducers: {
    increment: (state, payload) => state + payload,
    decrement: (state, payload) => state - payload
  },
  effects: {
    async incrementAsync(payload) {
      await delay();
      this.increment(payload);
    }
  }
};

const store = init({
  models: { count }
});

dispatch.count.incrementAsync(1);

3 精讀

我以為本文基本上把 redux 存在的工程題目剖析透闢了,同時還給出了一套異常好的完成。

細節的極致優化

首先是直接運用 payload 而不是全部 action 作為入參,加強了束縛同時簡化代碼龐雜度:

increment: (state, payload) => state + payload;

其次運用 async 在 effects 函數中,運用 this.increment 函數挪用體式格局,庖代 put({type: "increment"})(dva),在 typescript 中具有了範例支撐,不只能夠用自動跳轉替代字符串搜刮,還能校驗參數範例,在 redux 框架中異常難過。

末了在 dispatch 函數,也供應了兩種挪用體式格局:

dispatch({ type: "count/increment", payload: 1 });
dispatch.count.increment(1);

假如為了更好的範例支撐,或許屏障 payload 觀點,能夠運用第二種計劃,再一次簡化 redux 觀點。

內置了比較多的插件

rematch 將經常使用的 reselect、persist、immer 等都集成為了插件,相對比較強化插件生態的觀點。數據流對數據緩存,機能優化,開闢體驗優化都有進一步發揮的空間,擁抱插件生態是一個優越的發展方向。

比方 rematch-immer 插件,能夠用 mutable 的體式格局修正 store:

const count = {
  state: 0,
  reducers: {
    add(state) {
      state += 1;
      return state;
    }
  }
};

然則當 state 為非對象時,immer 將不起作用,所以最好能養成 return state 的習氣。

末了說一點瑕疵的處所,reducers 說明與挪用參數不一致。

Reducers 說明與挪用參數不一致

比方下面的 reducers:

const count = {
  state: 0,
  reducers: {
    increment: (state, payload) => state + payload,
    decrement: (state, payload) => state - payload
  },
  effects: {
    async incrementAsync(payload) {
      await delay();
      this.increment(payload);
    }
  }
};

定義時 increment 是兩個參數,而 incrementAsync 挪用它時,只要一個參數,如許能夠形成一些誤導,筆者發起堅持參數對應關聯,將 state 放在 this 中:

const count = {
  state: 0,
  reducers: {
    increment: payload => this.state + payload,
    decrement: payload => this.state - payload
  },
  effects: {
    async incrementAsync(payload) {
      await delay();
      this.increment(payload);
    }
  }
};

固然 rematch 的體式格局堅持了函數的無副作性子,能夠看出是做了一些棄取。

4 總結

反覆一下作者提出東西質量的公式:

東西質量 = 東西節約的時候/運用東西斲喪的時候

假如一個東西能節約開闢時候,但自身帶來了很大運用本錢,在想清晰怎樣削減運用本錢之前,不要急着用在項目中,這是我獲得的最大啟示。

末了謝謝 rematch 作者字斟句酌的精力,給 redux 帶來進一步的極致優化。

5 更多議論

議論地點是:
精讀《從新思索 Redux》 · Issue #83 · dt-fe/weekly

假如你想介入議論,請點擊這裏,每周都有新的主題,周末或周一宣布。

    原文作者:黃子毅
    原文地址: https://segmentfault.com/a/1190000014843214
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞