javascript 如何取消/忽略redux中的操作

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34184677/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 17:21:24  来源:igfitidea点击:

How to cancel/ignore an action in redux

javascriptfluxredux

提问by Stoikerty

Is there a way to cancel an action or ignore it?

有没有办法取消或忽略操作?

Or rather what is the best/recommended way to ignore an action?

或者更确切地说,忽略操作的最佳/推荐方法是什么?

I have the following action creator and when I input an invalid size (say 'some_string') into the action creator, in addition to getting my own warning message I also get: Uncaught Error: Actions must be plain objects. Use custom middleware for async actions.

我有以下动作创建者,当我向动作创建者输入无效大小(例如'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');
  }
};

I've discussed this in the redux-channel in Discord (reactiflux) where one suggestion was to use redux-thunk like this:

我已经redux在 Discord (reactiflux)的-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');
  }
}

The other option was to ignore the action inside the reducer. This does make the reducer "fatter" because it then has more responsibilities, but it uses less thunk-actions which makes it easier to debug. I could see the thunk-pattern getting out of hand since I would be forced to use it for almost every action, making batched actions a bit of a pain to maintain if you have lots of them.

另一种选择是忽略减速器内部的动作。这确实使reducer“变胖”,因为它有更多的责任,但它使用更少的thunk-actions,这使得调试更容易。我可以看到 thunk 模式失控,因为我将被迫在几乎每个动作中使用它,如果你有很多批处理动作,那么维护它会有点痛苦。

采纳答案by Tomá? Weiss

Ignoring actions in Action Creators is basically a way of treating them as Command Handlers, not Event Creators. When the User clicks the button it's some kind of Event though.

在 Action Creators 中忽略动作基本上是将它们视为命令处理程序而不是事件创建者的一种方式。当用户单击按钮时,它是某种事件。

So there are basically two ways how to solve the issue:

所以基本上有两种方法可以解决这个问题:

  1. The condition is inside action creator and thunk-middlewareis used

    const cancelEdit = () => (dispatch, getState) => {
      if (!getState().isSaving) {
        dispatch({type: CANCEL_EDIT});
      }
    }
    
  2. The condition is inside reducer and no middleware is required

    function reducer(appState, action) {
      switch(action.type) {
       case: CANCEL_EDIT:
         if (!appState.isSaving) {
           return {...appState, editingRecord: null }
         } else {
           return appState;
         }
       default:
         return appState;
    
      }
    }
    
  1. 条件在动作创建者内部thunk-middleware并被使用

    const cancelEdit = () => (dispatch, getState) => {
      if (!getState().isSaving) {
        dispatch({type: CANCEL_EDIT});
      }
    }
    
  2. 条件在reducer内部,不需要中间件

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

I strongly prefer treating UI interaction as Events instead of Commands and there two advantages:

我非常喜欢将 UI 交互视为事件而不是命令,并且有两个优点:

  1. All your domain logic stays in the synchronous pure reducers which are very easy to test. Just imagine you would need to write unit test for the functionality.

    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. 您所有的领域逻辑都保留在同步纯减速器中,这很容易测试。试想一下,您需要为该功能编写单元测试。

    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}
    );
    

As you can see the test is really simply when you treat the interaction as an Event

如您所见,当您将交互视为事件时,测试实际上很简单

  1. What if you decided that instead of ignoring the action you would rather show some visual indication that the action is not possible? You would need to dispatch another action or basically rebuild it. However, you can't use hot-reload with replay here because the logic in action creator is not re-playable. If the logic is in reducer though, you can simply change the behaviour, the reducer will get hot-reloaded and all the events gets replayed. The only event that you dispatch is that user clicked some button and you can't deny that fact. So unless you drastically change the UI you can always hot-reload with replay.
  1. 如果您决定与其忽略该操作,不如显示一些该操作不可能的视觉指示,该怎么办?您需要调度另一个动作或基本上重建它。但是,您不能在此处使用带重播的热重载,因为 action creator 中的逻辑不可重播。如果逻辑在 reducer 中,你可以简单地改变行为,reducer 将被热重载并且所有事件都被重播。您发送的唯一事件是用户单击了某个按钮,您不能否认这一事实。因此,除非您彻底更改 UI,否则您始终可以通过重播进行热重载。

When you think about any interaction with the UI as an Event then you will get the best possible replay experience, because Events can't be denied they have just happened.

当您将与 UI 的任何交互视为事件时,您将获得最佳的回放体验,因为无法否认事件刚刚发生。