Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async data and Flux stores #530

Closed
ksesong opened this issue Nov 27, 2014 · 6 comments
Closed

Async data and Flux stores #530

ksesong opened this issue Nov 27, 2014 · 6 comments

Comments

@ksesong
Copy link

ksesong commented Nov 27, 2014

I am using the Flux architecture. Each time I visit a page, the page requests data from a Store, but as the action is created (eg. REQUEST_TODO), there is no explicit callback triggered, but another action will be created once the request is completed (eg. REQUEST_TODO_COMPLETED).

I am trying to implement something similar to (https://github.com/rackt/react-router/blob/master/docs/api/run.md or https://github.com/rackt/react-router/blob/master/examples/async-data/app.js), I get the principles, but I struggle adapting with the concept of actions. Should I consider my actions differently, by adding some sort of callback, but this would make the dataflow more complex?

I am just starting, so this is likely I have missed something.
Thanks

@ColCh
Copy link

ColCh commented Nov 27, 2014

One action. There must be one action for async flow. This action dispathes two events - first, REQUEST_TODO fired before send, and other, REQUEST_TODO_COMPLETED - after send. (ok, you should handle error somehow also, e.x. REQUEST_TODO_FAILED)

Please take a look at Fluxxor tutorial about async Flux, it's very simple and clear.

PS. It this question related to react-router ?

@ksesong
Copy link
Author

ksesong commented Nov 27, 2014

Yes sorry, I mixed up when I wrote this issue. There is only one action, which triggers two events as you said. This is related to react-router, as I want the router to display the react component once he receives all the data from the server (ie. when REQUEST_TODO_COMPLETED is emitted). In the example linked, it uses callbacks, waiting that all promises are fulfilled before rendering the component with the filled data prop. I am wondering how I can apply this method to the Flux architecture, knowing that it deals with events...

@gaearon
Copy link
Contributor

gaearon commented Nov 27, 2014

Here's how I do it:

// Usage example:
//
// willTransitionTo(transition, params) {
//   transition.wait(
//     observeStore(
//       DraftStore,
//       s => s.isLoaded()
//     ).then(() => {
//       if (DraftStore.isMissingTitle()) {
//         transition.redirect('composeDraft', params);
//       }
//     })
//   );
// }


'use strict';

var Promise = require('bluebird');

function observeStore(store, predicate) {
  var performCheck;

  return new Promise(resolve => {
    performCheck = () => {
      if (predicate.call(null, store)) {
        resolve();
      }
    };

    store.addChangeListener(performCheck);
    performCheck();

  }).finally(() => {
    store.removeChangeListener(performCheck);
  });
}

module.exports = observeStore;

You can do observeStore(TodoStore, store => !store.isRequesting()), for example.

@ryanflorence
Copy link
Member

I find flux interesting in some cases, and totally nuts in others (like using it to get the router to render), but if you think its important, I think it'd look something like this:

var DataStore = require('DataStore');
var RouterStore = require('RouterStore');

RouteActionCreators = {
  routeChange (Handler, state) {
    // 2. web api util fetches data
    DataUtil.fetchData(state.params);
    // 3. dispatcher dispatches action
    dispatchAction('ROUTE_CHANGE', {Handler, state});
    // 4. RouterStore listens for 'ROUTE_CHANGE' and stores
    //    `Handler` and `state` for subscribers
    // 5. DataUtil dispatches 'DATA_LOADED' and then `DataStore`
    //    grabs the data from that payload
  }
};


// 6. we have subscribed here to the data store, to render when
//    the data changes
DataStore.addChangeListener(render);

function render() {
  // 7. get the handler off the RouterStore
  var { Handler } = RouterStore.getState();
  // 8. and the data
  var data = DataStore.getState();
  // 9. render
  React.render(<Handler data={data}/>, document.body);
}

Router.run(routes, (Handler, state) => {
  // 1. route changes call an action with payload
  RouterActionCreators.routeChange(Handler, state);
});

@kellyrmilligan
Copy link
Contributor

Just wanted to say thanks for this, it helped me with integrating fluxxor into my app with react router.

for anyone else:

var React = require('react');
var Router = require('react-router');
var routes = require('./routes');
var flux = require('./flux');

var appStore = flux.store('AppStore');
var viewRoot = document.getElementById('content');
var initialState = JSON.parse(document.getElementById('state').innerHTML);

appStore.on('change', function () {

  var Handler = appStore.state.handler;
  var data = appStore.state.data;

  React.render(<Handler state={data}/>, viewRoot);

});

Router.run(routes, Router.HistoryLocation, function (Handler, state) {

  if (initialState.isInitial) {
    delete initialState.isInitial;
    flux.actions.hydrate({ initialState: initialState, handler: Handler, state: state});
  } else {
    flux.actions.request({ state: state, handler: Handler});
  }

});

@wmyers
Copy link

wmyers commented Jun 12, 2015

@gaearon thanks for this technique.

I have a login with an async call to fetch user data with a token (which might be stored locally). I'm using the observeStore in a willTransitionTo for a page that requires authentication as follows.

 static willTransitionTo(transition, params, query, callback) {
      if (!LoginStore.isLoggedIn()) {
        //store next path in RouterStore for redirecting after authentication
        let transitionPath = transition.path;
        RouterActionCreators.storeRouterTransitionPath(transitionPath);

        //check if still waiting for a user data request
        observeStore(LoginStore, s => !s.isRequestingUserData())
        .then(() => {
          if (!LoginStore.isLoggedIn()) {
            transition.redirect('/login');
          }
        })
        .finally(callback);
      }else{
        callback();
      }
    }

@lock lock bot locked as resolved and limited conversation to collaborators Jan 24, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants