减速器为了传达操作的部分处理而改变其操作对象是否可以接受?

Gui*_*rie 5 javascript separation-of-concerns reactjs redux

我\xe2\x80\x99m 在 React/Redux/Redux Toolkit 中编写了一个非常复杂的应用程序,我遇到了一种情况,我\xe2\x80\x99m 不太确定如何处理。我找到了一种方法来做到这一点,但我\xe2\x80\x99m想知道它是否会导致问题或者是否有更好的方法。简而言之,我希望减速器能够在不修改状态的情况下与调用者进行通信,而我发现的唯一方法是改变操作。

\n

描述:

\n

为了简化,让\xe2\x80\x99s说我想实现一个水平滚动条(但实际上它\xe2\x80\x99s要复杂得多)。状态包含当前位置、一个介于某些minmax值之间的数字,UI 绘制一个具有该位置并且可以单击并水平拖动的矩形。

\n

主要属性:如果用户单击并拖动的距离超过最小/最大值,则矩形不会进一步移动,但如果用户随后向另一个方向移动,则矩形应等到鼠标回到其原始位置之前开始向后移动(与大多数/所有操作系统上的滚动条行为完全相同)。

\n

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

\n

限制条件:

\n
    \n
  • 我不希望 UI/组件/自定义挂钩有责任在达到最小值/最大值时进行计算,因为在我的用例中,它可能非常复杂并且取决于状态的各个部分。因此,reducer 是唯一知道我们是否达到最小值/最大值的地方。
  • \n
  • 另一方面,为了实现上面的Main 属性,我确实需要以某种方式记住我们在矩形上单击的位置,或者处理给定“拖动”操作的像素数,以便知道何时开始向后移动。但我不想\xe2\x80\x99t 想要将其存储在状态中,因为\xe2\x80\x99s 实际上是一个不\xe2\x80\x99t 属于那里的UI 细节(也因为我有很多不同的情况,其中我需要这样做,我的状态将变得更加复杂,并且不必要的状态更改将导致性能下降)。
  • \n
\n

问题:

\n

因此,减速器是唯一知道我们是否达到最小值/最大值的部分,并且减速器通常与应用程序其余部分通信的唯一方式是通过状态,但我不想\xe2\x80\x99 想要传达该信息通过国家。

\n

解决方案?

\n

我实际上设法找到了一种方法来解决它,这似乎工作得很好,但感觉有些错误:改变减速器中的操作对象。

\n

减速器采用“拖动 10 个像素”的动作,意识到它只能拖动 3 个像素,创建一个已拖动 3 个像素的新状态,并向该动作添加一个字段。action.response = 3

\n

然后,在我的自定义挂钩分派“拖动 10 像素”操作后,它会查看action.response返回值的字段,dispatch以了解实际处理了多少内容,并且它会记住与预期值的差异(在本例中,它会记住我们距原始位置 7 个像素)。\n这样,如果在下一次鼠标移动时我们拖动 -9 像素,我的自定义钩子可以将该数字添加到它记住的 7 像素中,并告诉减速器我们只移动了-2 像素。

\n

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

\n
    \n
  • 减速器只需要知道我们移动了多少像素,然后返回新状态以及实际处理了多少像素(通过改变动作)
  • \n
  • 自定义钩子可以记住我们距离原始位置有多远(无需知道原因),然后它会简单地纠正event.movementX以补偿减速器在之前的操作中没有\xe2\x80\x99t处理的量,然后发送减速器的正确增量。
  • \n
\n

它也可以很好地处理诸如每 100 像素捕捉一次之类的事情。

\n

唯一奇怪的是,reducer 改变了操作,我认为这不应该发生,因为它应该是一个纯函数,但到目前为止我无法\xe2\x80\x99 发现任何问题。该应用程序可以正常工作,Redux Toolkit 不会\xe2\x80\x99 抱怨,并且开发工具也可以正常工作。

\n

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

\n

还有其他方法可以做到吗?

\n

mar*_*son 3

在技​​术层面上,我可以看到这是如何运作的。但我也同意这感觉“恶心”。 技术上讲,改变操作本身就属于一种“副作用”,尽管它不会有意义地破坏应用程序的其余部分。

听起来好像这里的关键逻辑更多是在“调度操作”级别。我认为您可能可以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
  }
}
Run Code Online (Sandbox Code Playgroud)

这样,只有 UI 层关心这些变化。