web-dev-qa-db-ja.com

reduxのアクションをキャンセル/無視する方法

アクションをキャンセルまたは無視する方法はありますか?

むしろ、アクションを無視するための最良/推奨される方法は何ですか?

次のアクション作成者がいて、無効なサイズを入力すると('some_string')アクションクリエータに、独自の警告メッセージを表示するだけでなく、Uncaught Error: Actions must be plain objects. Use custom middleware for async actions.

import { SET_SELECTED_PHOTOS_SIZE } from './_reducers';

export default (size=0) => {
  if (!isNaN(parseFloat(size))) {
    return {
      type: SET_SELECTED_PHOTOS_SIZE,
      size: size,
    };
  } else {
    app.warn('Size is not defined or not a number');
  }
};

Discord(reactiflux)のredux- channelでこれについて説明しましたが、ここでは、次のようにredux-thunkを使用することを提案しました。

export default size => dispatch => {
  if (!isNaN(parseFloat(size))) {
    dispatch({
      type: SET_SELECTED_PHOTOS_SIZE,
      size: size,
    });
  } else {
    app.warn('Size is not defined or not a number');
  }
}

もう1つのオプションは、レデューサー内のアクションを無視することでした。これは、より多くの責任を負うため、レデューサーを「太く」しますが、サンクアクションの使用を減らし、デバッグを容易にします。サンクパターンはほとんどすべてのアクションで使用する必要があるため、手に負えなくなることがわかりました。バッチアクションがたくさんある場合は、維持するのが少し面倒です。

17
Stoikerty

アクションクリエーターでアクションを無視することは、基本的にそれらをイベントクリエーターではなく、コマンドハンドラーとして扱う方法です。ユーザーがボタンをクリックすると、それはある種のイベントです。

したがって、問題を解決する方法は基本的に2つあります。

  1. 条件はアクション作成者とthunk-middleware 使用されている

    const cancelEdit = () => (dispatch, getState) => {
      if (!getState().isSaving) {
        dispatch({type: CANCEL_EDIT});
      }
    }
    
  2. 状態はレデューサー内にあり、ミドルウェアは必要ありません

    function reducer(appState, action) {
      switch(action.type) {
       case: CANCEL_EDIT:
         if (!appState.isSaving) {
           return {...appState, editingRecord: null }
         } else {
           return appState;
         }
       default:
         return appState;
    
      }
    }
    

私は、UIインタラクションをコマンドではなくイベントとして扱うことを強くお勧めします。2つの利点があります。

  1. すべてのドメインロジックは、テストが非常に簡単な同期の純粋なレデューサーにとどまります。機能の単体テストを作成する必要があると想像してみてください。

    const state = {
      isSaving: true,
      editingRecord: 'FOO'
    };
    
    // State is not changed because Saving is in progress
    assert.deepEqual(
      reducer(state, {type: 'CANCEL_EDIT'}),
      state
    );
    
    // State has been changed because Saving is not in progress anymore
    assert.deepEqual(
      reducer({...state, isSaving: false}),
      {isSaving: false, editingRecord: null}
    );
    

ご覧のとおり、相互作用はイベントとして扱う場合、テストは本当に単純です

  1. アクションを無視するのではなく、アクションが不可能であることを視覚的に示すことを決定した場合はどうでしょうか。別のアクションをディスパッチするか、基本的にそれを再構築する必要があります。ただし、アクションクリエーターのロジックは再生できないため、ここで再生とホットリロードを使用することはできません。ただし、ロジックがレデューサーにある場合は、動作を変更するだけで、レデューサーがホットリロードされ、すべてのイベントが再生されます。ディスパッチする唯一のイベントは、ユーザーがボタンをクリックしたことであり、その事実を否定することはできません。したがって、UIを大幅に変更しない限り、リプレイで常にホットリロードできます。

イベントとしてのUIの操作を考えると、イベントは発生したことを否定できないため、可能な限り最高の再生体験を得ることができます。

14
Tomáš Weiss