reducer 改变其动作对象以传达动作的部分处理是否可以接受?

Is it acceptable for a reducer to mutate its action object in order to communicate partial handling of the action?

我正在 React/Redux/Redux Toolkit 中编写一个非常复杂的应用程序,我遇到了一种我不太确定如何处理的情况。我找到了一种方法来做到这一点,但我想知道它是否会导致问题或者是否有更好的方法。简短的版本是我希望 reducer 在不修改状态的情况下与调用者通信,我发现的唯一方法是改变动作。

描述:

为了简单起见,假设我想实现一个水平滚动条(但实际上要复杂得多)。状态包含当前位置,一个上限介于 minmax 值之间的数字,UI 绘制一个具有该位置且可以水平单击和拖动的矩形。

Main 属性:如果用户点击并拖拽的距离超过min/max值,那么矩形不会再移动,但是如果用户然后向另一个方向移动,矩形应该等到鼠标回到原来的位置,然后再开始向后移动(就像 most/all 操作系统上的滚动条一样)。

请记住,我的实际用例要复杂得多,我有很多类似的情况,有时在最小值和最大值之间封顶,有时每 100 个像素捕捉一次,有时更复杂的约束取决于图像的各个部分状态等。我想要一个适用于所有此类情况并保留 UI 和逻辑之间的分离的解决方案。

约束条件:

问题:

所以 reducer 是唯一知道我们是否到达 min/max 的部分,并且 reducer 通常与应用程序其余部分通信的唯一方式是通过状态,但我不想通过国家传达该信息。

解决方案?

我实际上设法找到了解决它的方法,它似乎工作得很好但感觉有点不对劲:改变减速器中的动作对象。

reducer采取“拖10个像素”的动作,意识到它只能拖3个像素,创建一个新的状态,它被拖了3个像素,添加 一个 action.response = 3 字段到操作。

然后在我的自定义挂钩发出“拖动 10 个像素”动作后,它会查看 dispatch 的 return 值的 action.response 字段以了解实际有多少处理后,它会记住与预期值的差异(在这种情况下,它会记住我们距离原始位置 7 个像素)。 这样,如果在下一次 mousemove 时我们拖动 -9 像素,我的自定义钩子可以将该数字添加到它记住的 7 像素,并告诉 reducer 我们只移动了 -2 像素。

在我看来,这个解决方案完美地保留了 UI/logic 的分离:

它也适用于每 100 像素捕捉等情况。

唯一奇怪的是 reducer 改变了动作,我认为这不应该发生,因为它应该是一个纯函数,但到目前为止我找不到任何问题。该应用程序运行正常,Redux Toolkit 没有报错,开发工具也运行良好。

这个解决方案有什么问题吗?

还有其他方法吗?

在技术层面上,我可以看到它是如何工作的。但我也同意它感觉“恶心”。 非常 从技术上讲,改变操作本身可以称为“副作用”,尽管它不会有意义地破坏应用程序的其余部分。

听起来好像这里的逻辑关键位更多地处于“调度操作”级别。我认为您可能会在分派之前和之后调用 getState() 来比较结果,并以这种方式导出所需的额外数据。事实上,这可能是 thunk 的一个很好的用例:

const processDragEvent = (dragAmountPixels: number) => {
  return (dispatch, getState) => {
    const stateBefore = getState();
    dispatch(dragMoved(dragAmountPixels));
    const stateAfter = getState();

    const actualAmountChanged = selectActualDragAmount(stateBefore, stateAfter);

    // can return a result from the thunk
    return actualAmountChanged

  }
}

// later, in a hook
const useMyCustomDragBehavior = () => {
  const dispatch = useDispatch();
  const doSomeDragging = (someDragValue: number) => {
    const actualAmountDragged = dispatch(processDragEvent (someDragValue));
    // do something useful with this info here
  }
}

这样只有 UI 层与更改有关。