简体   繁体   中英

What's the idiomatic way to keep UI in sync with ajax calls when using Flux?

The general problem: Let's say I have a button with an onClick handler calling an action creator. The action does an ajax call which dispatches a message when ajax responds, and this in some way affects the UI. Given this basic pattern there's nothing stopping the user from clicking this button multiple times, and thus running the ajax call multiple times.

This is something that doesn't seem to be touched upon in the React or Flux documentation (as far as I have seen), so I've tried to come up with some methods on my own.

Here are those methods

  • Use lodash.throttle on a method which does an ajax call so that multiple clicks in quick succession don't create multiple calls.
  • Use lodash.debounce on a method so that ajax is only called once a user hasn't done any activity for a bit. This is how I'm doing semi-realtime updates of text fields on change.
  • Dispatch an "is updating" message to stores when the action is first called and then dispatch a "done" message when the ajax call returns. Do stuff like disabling input on the initial message and then re-enable on the second.

The third method seems to be the best in terms of functionality since it allows you to make the user interface reflect exactly what's going on, but it's also incredibly verbose. It clutters absolutely everything up with tons of extra state, handler methods, etc...

I don't feel like any of these methods are really idiomatic. What is?

I would argue that the third method is the correct way, but I don't find it to be verbose. A lot of React code that I see written sort of misses the spirit of React with its idea of very small, composable components. When large monolithic components are created, yes, things can get very messy.

But if the button in question is its own component, then it can take care of rendering based on its state. When a user clicks the button, the state of just that component changes -- and it renders it in a way that it can't be clicked again.

Once the store has notified that component that it has changed, the component can set its state back -- and with it, re-render itself.

It's a pretty straight-forward process; it just requires thinking about pages as a collection of small, composable units.

Hal is pretty much correct. Dispatching multiple messages is the Fluxiest way to go.

However, I would be wary of dispatching an IS_UPDATING message. This makes reasoning about your code harder because for each AJAX action you're dispatching several actions at once.

The idiomatic solution is to split your AJAX "actions" (action-creator-actions) into three dispatched actions: MY_ACTION , MY_ACTION_SUCCESS , MY_ACTION_FAILURE , handling each instance appropriately, and tracking "pending-ness" along the way.

For example:

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

This gives you several benefits over your alternate solutions:

  1. Your store is exclusively in control of whether an item is pending or not. You don't have to worry about changing UI state on actions in your UI code: you can have your UI look exclusively to a pending property of your store for truth. This is probably the biggest reason for using Flux over MVC systems.
  2. You have a clean interface for taking your actions. It's easy to reason about and easy to attach other stores to this data (if you have a LatestMessageStore or something, it's easy to subscribe to these events). This is the benefit over using IS_UPDATING as Hal suggested.
  3. You save your lodash calls for when they semantically make sense— like when you may be inundated with legitimate data (a text field).
  4. You can easily switch between optimistic updates (change the store when POST_MESSAGE is called) or pessimistic updates (change the store on POST_MESSAGE_SUCCESS ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM