使用 Flux 时保持 UI 与 ajax 调用同步的惯用方法是什么?
What's the idiomatic way to keep UI in sync with ajax calls when using Flux?
一般问题:假设我有一个带有调用动作创建器的 onClick 处理程序的按钮。该操作执行 ajax 调用,在 ajax 响应时发送消息,这在某种程度上影响了 UI。鉴于此基本模式,没有什么可以阻止用户多次单击此按钮,因此 运行 ajax 调用多次。
这在 React 或 Flux 文档中似乎没有涉及(据我所知),所以我尝试自己想出一些方法。
这是这些方法
- 在执行 ajax 调用的方法上使用 lodash.throttle,这样快速连续的多次单击就不会创建多个调用。
- 在方法上使用 lodash.debounce,这样 ajax 仅在用户暂时未执行任何 activity 时才被调用。这就是我在更改时对文本字段进行半实时更新的方式。
- 在首次调用操作时向存储发送 "is updating" 消息,然后在 ajax 调用 returns 时发送 "done" 消息。执行诸如在初始消息上禁用输入然后在第二个消息上重新启用之类的操作。
第三种方法在功能方面似乎是最好的,因为它允许您使用户界面准确反映正在发生的事情,但它也非常冗长。它用大量的额外状态、处理程序方法等把所有东西搞得一团糟……
我不觉得这些方法中有任何一个是真正地道的。什么是?
我认为第三种方法是正确的方法,但我认为它并不冗长。我看到的很多 React 代码都与 React 的精髓相悖,因为它的想法是非常小的、可组合的组件。当创建大型单体组件时,是的,事情会变得非常混乱。
但是如果有问题的按钮是它自己的组件,那么它可以根据其状态进行渲染。当用户单击该按钮时,只有该组件的状态会发生变化——并且它会以一种无法再次单击的方式呈现它。
一旦商店通知该组件它已更改,该组件就可以设置它的状态——并重新呈现它自己。
这是一个非常简单的过程;它只需要将页面视为可组合的小单元的集合。
Hal 非常正确。分派多条消息是最灵活的方式。
但是,我会谨慎发送 IS_UPDATING
消息。这使得对您的代码进行推理变得更加困难,因为对于每个 AJAX 操作,您将同时分派多个操作。
惯用的解决方案是将您的 AJAX "actions" (action-creator-actions) 分成三个分派的动作:MY_ACTION
、MY_ACTION_SUCCESS
、MY_ACTION_FAILURE
,适当地处理每个实例,并一路跟踪 "pending-ness"。
例如:
// MyActionCreator.js
// because this is in a closure, you can even use the promise
// or whatever you want as a sort of "ID" to handle multiple
// requests at one time.
postMessage() {
dispatch('POST_MESSAGE', { ... } );
api.slowMessagePostingAjaxThingy().then(
(success) => { dispatch('POST_MESSAGE_SUCCESS', { ... }); },
(failure) => { dispatch('POST_MESSAGE_FAILURE', { ... }); }
);
}
// MyStore.js
on('POST_MESSAGE', (payload) => { /* do stuff */ });
on('POST_MESSAGE_SUCCESS', (payload) => { /* handle success */ });
on('POST_MESSAGE_FAILURE', (payload) => { /* handle failure */ });
与其他解决方案相比,这为您提供了多项优势:
- 您的商店完全控制商品是否处于待处理状态。您不必担心在您的 UI 代码中更改操作的 UI 状态:您可以让您的 UI 只查看
pending
属性你的真理商店。这可能是在 MVC 系统上使用 Flux 的最大原因。
- 你有一个干净的界面来执行你的操作。很容易推理并且很容易将其他商店附加到此数据(如果您有
LatestMessageStore
或其他东西,则很容易订阅这些事件)。这是 Hal 建议的使用 IS_UPDATING
的好处。
- 当它们在语义上有意义时,您可以保存您的 lodash 调用——比如当您可能被合法数据(文本字段)淹没时。
- 您可以轻松地在乐观更新(在调用
POST_MESSAGE
时更改存储)或悲观更新(在 POST_MESSAGE_SUCCESS
上更改存储)之间轻松切换。
一般问题:假设我有一个带有调用动作创建器的 onClick 处理程序的按钮。该操作执行 ajax 调用,在 ajax 响应时发送消息,这在某种程度上影响了 UI。鉴于此基本模式,没有什么可以阻止用户多次单击此按钮,因此 运行 ajax 调用多次。
这在 React 或 Flux 文档中似乎没有涉及(据我所知),所以我尝试自己想出一些方法。
这是这些方法
- 在执行 ajax 调用的方法上使用 lodash.throttle,这样快速连续的多次单击就不会创建多个调用。
- 在方法上使用 lodash.debounce,这样 ajax 仅在用户暂时未执行任何 activity 时才被调用。这就是我在更改时对文本字段进行半实时更新的方式。
- 在首次调用操作时向存储发送 "is updating" 消息,然后在 ajax 调用 returns 时发送 "done" 消息。执行诸如在初始消息上禁用输入然后在第二个消息上重新启用之类的操作。
第三种方法在功能方面似乎是最好的,因为它允许您使用户界面准确反映正在发生的事情,但它也非常冗长。它用大量的额外状态、处理程序方法等把所有东西搞得一团糟……
我不觉得这些方法中有任何一个是真正地道的。什么是?
我认为第三种方法是正确的方法,但我认为它并不冗长。我看到的很多 React 代码都与 React 的精髓相悖,因为它的想法是非常小的、可组合的组件。当创建大型单体组件时,是的,事情会变得非常混乱。
但是如果有问题的按钮是它自己的组件,那么它可以根据其状态进行渲染。当用户单击该按钮时,只有该组件的状态会发生变化——并且它会以一种无法再次单击的方式呈现它。
一旦商店通知该组件它已更改,该组件就可以设置它的状态——并重新呈现它自己。
这是一个非常简单的过程;它只需要将页面视为可组合的小单元的集合。
Hal 非常正确。分派多条消息是最灵活的方式。
但是,我会谨慎发送 IS_UPDATING
消息。这使得对您的代码进行推理变得更加困难,因为对于每个 AJAX 操作,您将同时分派多个操作。
惯用的解决方案是将您的 AJAX "actions" (action-creator-actions) 分成三个分派的动作:MY_ACTION
、MY_ACTION_SUCCESS
、MY_ACTION_FAILURE
,适当地处理每个实例,并一路跟踪 "pending-ness"。
例如:
// MyActionCreator.js
// because this is in a closure, you can even use the promise
// or whatever you want as a sort of "ID" to handle multiple
// requests at one time.
postMessage() {
dispatch('POST_MESSAGE', { ... } );
api.slowMessagePostingAjaxThingy().then(
(success) => { dispatch('POST_MESSAGE_SUCCESS', { ... }); },
(failure) => { dispatch('POST_MESSAGE_FAILURE', { ... }); }
);
}
// MyStore.js
on('POST_MESSAGE', (payload) => { /* do stuff */ });
on('POST_MESSAGE_SUCCESS', (payload) => { /* handle success */ });
on('POST_MESSAGE_FAILURE', (payload) => { /* handle failure */ });
与其他解决方案相比,这为您提供了多项优势:
- 您的商店完全控制商品是否处于待处理状态。您不必担心在您的 UI 代码中更改操作的 UI 状态:您可以让您的 UI 只查看
pending
属性你的真理商店。这可能是在 MVC 系统上使用 Flux 的最大原因。 - 你有一个干净的界面来执行你的操作。很容易推理并且很容易将其他商店附加到此数据(如果您有
LatestMessageStore
或其他东西,则很容易订阅这些事件)。这是 Hal 建议的使用IS_UPDATING
的好处。 - 当它们在语义上有意义时,您可以保存您的 lodash 调用——比如当您可能被合法数据(文本字段)淹没时。
- 您可以轻松地在乐观更新(在调用
POST_MESSAGE
时更改存储)或悲观更新(在POST_MESSAGE_SUCCESS
上更改存储)之间轻松切换。