Reduxの悪い部分を殺すこと.boilerplateにさよならを言ってください.


Reduxは、JavaScriptの生態系の中で最も人気のある国家管理ライブラリは、単一のページアプリケーションです.しかし、おそらくreduxのような悪名高いステートメントではない場合は、はるかに人気があるでしょう、冗長な、reduxのboilerplateなど.私の意見では、reduxの1つだけの部分は、使いやすい、すなわちreduxアクションです.この記事では、いくつかの問題をreduxのアクションと私たちはそれらを緩和するために何を行うことができますポイントしてください.

必ずしもreduxの冗長な部分ではありません
私たちが始める前に、詳細な話をしよう.

別の行動と減速
Reduxでは、アクションとリダクションを別々に書く必要があると多くの人々が不平を言います.私にとって、これは良いことです、そして、実はこれはデザインによってされました.我々は、行動と減速者が1〜1の関係を持つと考えるべきではありません.つの還元剤は、多くの別々の行動に反応することができます、そして、多くの還元剤は非常に同じ行動に反応することができます.これはreduxの最も強力な機能の一つで、しばしば評価されません.

還元器におけるswitch文
我々の多くは憎みますswitch 還元剤の記載しかしこれは独断的ですが、さまざまな方法で還元器を書くことができるライブラリがたくさんあります.私たちは、この記事で少し後でそのようなヘルパーを書きます!

Reduxの真に冗長な部分
私にとって、reduxの最も問題のある部分はアクション、定数、およびthunksに関連しています.さらに、それらの問題は冗長性だけでなく、タイプ衝突のような潜在的なバグについてもあります.これらの問題に名前を付けて、何も残っていないまで、それらを一つずつ修正しようとしましょう!

定数
私の頭の中で、これは常にreduxで最も厄介なことだった.別々のアクションと定数を書くのは冗長ではなく、エラーがちです.また、我々の輸入にいくつかの障害も紹介します.たとえば、アクションを認識する定数が必要ですが、アクションを起こす必要があります(アクションの作成者は正確ですが、シンプルにするためのアクションショートカットに固執させてください).多くの場合、アクションと定数は非常に同じアクションに関連するインポートを終了!どのような妥協せずに定数を完全に与えることができれば?ヘルパー機能を書きましょう!
const createAction = (name, action = () => ({})) => {
  const actionCreator = (...params) => ({
    type: name,
    ...action(...params),
  });
  actionCreator.toString = () => name;
  return actionCreator;
};
それで、我々はちょうど何をしましたか?説明する代わりに、それを使用しようとしましょう!そのような行動があると想像してください.
const INCREMENT_BY_VALUE = 'INCREMENT_BY_VALUE';

const incrementByValue = value => ({
  type: INCREMENT_BY_VALUE,
  value,
)};
今のように書き直すことができました.
const incrementByValue = createAction(
  'INCREMENT_BY_VALUE',
  value => ({ value }),
);
ご覧の通り、我々は通りますINCREMENT_BY_VALUE 第1引数createAction , 残りは私たちの仕事です.しかし、2番目のを待つ、我々はもう定数を持っていないので、我々はどのように減速機でそれを使用することができますか?キーはactionCreator.toString = () => name ラインインcreateAction ボディは、アクションタイプの定数を得ることができますincrementByValue.toString() . だから、アクションは、同じタイプの元のソースですので、同期して定数とアクションを維持し、あなただけのアクションが必要です!ボーナスとして、時々あなたも呼び出す必要はありませんてtoString() 手動で、次の段落での方法を参照してください!

マニュアルの回避toString 減速者の呼び出し
この問題を解決する前にincrementByValue アクションは次のようになります.
const valueReducer = (state = 0, action) => {
  switch (action.type) {
    case incrementByValue.toString():
      return state + action.value;
    default:
      return state;
  }
};
標準を使うswitch 何人かの人々が好きで、一部の人々が憎む声明、通常の減速者と比較して唯一の問題は、この不快ですincrementByValue.toString() , 適切なものを得るためにはINCREMENT_BY_VALUE 種類幸運にもswitch and toString hatersには解決策があります.
const createReducer = (handlers, defaultState) => {
  return (state, action) => {
    if (state === undefined) {
      return defaultState;
    }

    const handler = handlers[action.type];

    if (handler) {
      return handler(state, action);
    }

    return state;
  };
};
今、我々はリファクタリングvalueReducer AS :
const valueReducer = createReducer({
  [incrementByValue]: (state, action) => state + action.value,
}, 0);
ご覧の通り、Noswitch or toString もう!交換したからswitch with handlers オブジェクト、計算されたプロパティを使用できます[incrementByValue] , コールtoString 自動的に!

サンクス
多くの開発者にとって、thunksは副作用を作成するのに用いられますredux-saga 図書館.私にとって、彼らはもっと何かです.多くの場合、私の行動には議論が必要ですが、すでにReduxストアに存在するような議論が必要です.再び、これについて多くの意見があります、しかし、私にとって、行動に通っているものは、すでに店に存在する何かが反パターンです.なぜ?あなたが反応でReduxを使用して、反応からアクションを派遣想像してください.このアクションがすでにストアに保管されている何かを渡す必要があると想像してください.何をするかあなたがこの値を読むuseSelector , connect または最初のような何か、ちょうどアクションに渡すために.多くの場合、このコンポーネントはそれを行う必要さえありません.なぜなら、この値はアクションの依存性であり、コンポーネントを直接反応しないからです.Reduxアクションが直接状態を読むことができれば、この反応コンポーネントははるかに簡単になる可能性があります!だから…救助にthunks!を書きましょう!
const incrementStoredValueByOne = () => (dispatch, getState) => {
  const { value } = getState(); // we could use selector here
  return dispatch({
    type: 'INCREMENT_STORED_VALUE_BY_ONE',
    newValue: value + 1,
  });
};
我々が続ける前に、もちろん、この例はあまりにもナイーブになるかもしれません、我々は還元器の適当な論理によってこの問題を解決することができました、それは問題を例証するだけです.とにかく、このthunkが引数としてそれを得るのではなく、ストアから現在の値を読み込むことに注意してください.その後解決した問題!それほど速くない!再び、タイプはどうですか?reduxから直接状態を読むためにthunkに動作をリファクタリングする必要があるならばcreateAction 再び.では、どうすればよいのでしょうか.似ていますが、ちょうどthunksのために!
const createThunk = (name, thunk) => {
  const thunkCreator = (...params) => (dispatch, getState) => {
    const actionToDispatch = thunk(...params)(dispatch, getState);
    return dispatch({ type: name, ...actionToDispatch });
  };

  thunkCreator.toString = () => name;
  return thunkCreator;
};
今、我々はそのような我々のthunkをリファクションすることができました:
const incrementStoredValueByOne = createThunk(
  'INCREMENT_STORED_VALUE_BY_ONE',
  () => (dispatch, getState) => {
    const { value } = getState(); // we could use selector here
    return { newValue: value + 1 };
  },
};
再び、定数なし!incrementStoredValueByOne.toString()INCREMENT_STORED_VALUE_BY_ONE , だから、直接あなたの減速機でこのthunkを聞くことができる!

その他の問題
私たちは既に多くの問題を解決しましたが、残念ながらもっと多くのことがあります.
  • あなたはまだアクションタイプを渡す必要がありますcreateAction or createThunk 最初の引数としては、重複の種類です.私たちがアクションを定義することができるならば、それはクールですconst myAction = createAction() の代わりにconst myAction = createAction('MY_ACTION')
  • どのような行動の種類の衝突のリスクは?あなたの行動の2つが非常に同じ名前を持つならば、どうですか?より大きなアプリケーションは、これが起こる可能性が大きいチャンス.ライブラリには、たとえば、型にカウンタを追加することによって、それを修正しようとします.しかし、それらの解決策は決定論的ではなく、ホットモジュール置換とサーバサイドレンダリングでのトラブルを引き起こす.
  • createAction and createThunk 何らかのタイプの型を持つ必要がある場合は、Visual Studioコードのようなテキストエディターで適切な自動補完を取得しません.
  • 我々は本当にアプリケーションを書くの間にそれらのことを気にする必要がありますか?我々は解決策を使用する準備ができている必要があります!
  • 幸いなことに、今このような解決策が存在します.

    導入redux-smart-actions 図書館
    紹介しますredux-smart-actions ライブラリは、最速の方法Reduxアクションを書く!
    このライブラリはすべてのユーティリティを提供しますcreateAction , createThunk , createReducer , そして、同時に、この記事でカバーされないすべての言及された問題を解決します.ポイント1と2はオプションbabel-plugin-redux-smart-actions . ポイント3は、タイプスクリプトタイプがライブラリに含まれるとき、解決されます.そして、ポイント4は、いずれかのライブラリによって解決されます.
    基本的には、そのようなコードを変換することができます.
    + import {
    +   createSmartAction,
    +   createSmartThunk,
    +   createReducer,
    +   joinTypes,
    + } from 'redux-smart-actions';
    +
    - const RESET_VALUE = 'RESET_VALUE';
    - const SET_VALUE = 'SET_VALUE';
    - const INCREMENT_IF_POSITIVE = 'INCREMENT_IF_POSITIVE';
    -
    - const resetValue = () => ({ type: RESET_VALUE });
    + const resetValue = createSmartAction();
    
    - const setValue = value => ({ type: SET_VALUE, value });
    + const setValue = createSmartAction(value => ({ value }));
    
    - const incrementIfPositive = () => (dispatch, getState) => {
    + const incrementIfPositive = createSmartThunk(() => (dispatch, getState) => {
        const currentValue = getState().value;
    
        if (currentValue <= 0) {
          return null;
        }
    
    -   return dispatch({
    -     type: INCREMENT_IF_POSITIVE,
    -     value: currentValue + 1,
    -   });
    +   return { value: currentValue + 1 });
    - };
    + });
    
    - const valueReducer = (state = 0, action) => {
    -   switch (action.type) {
    -     case RESET_VALUE:
    -       return 0;
    -     case SET_VALUE:
    -     case INCREMENT_IF_POSITIVE:
    -       return action.value;
    -     default:
    -       return state;
    -   }
    - }
    + const valueReducer = createReducer({
    +   [resetValue]: () => 0,
    +   [joinTypes(setValue, incrementIfPositive)]: (state, action) => action.value;
    + }, 0);
    
    このライブラリは新しいことを恐れてはいけない、私はいくつかの非常に大きなプロジェクトではすでに任意の問題がなく、それを使用するので、私は非常に少なくともそれをお試しください!あなたがそれのように起こるならば、Gitthubレポに星を与えるような感謝のどんなトークンも、非常に歓迎です!