Jophin Joseph
Jophin Joseph

Reputation: 2953

How to pause the delay of a component before state transition?

I have a set of AJAX requests to be called before the page can be rendered. I'm using react-router 2.4.1. In one of my previous projects, that used an older version of react-router, This is how I used to deal with this problem then

Router.run(RouteConfig, function(Handler, state){
    var promises = state.routes.filter(function(route) {
        //Every component that needs initial data for rendering will 
        //define a function fetchData in its statics which will return
        //a promise that will be resolved once all required AJAX calls             
        //are made.
        return route.handler.fetchData;
    }).map(function(route) {
        return route.handler.fetchData(state.params, state.query);
    });
    if(promises.length > 0) {
        Promise.all(promises).then(function(response) {
            data = response;
            //Rendering will happen only after every call is resolved
            render(Handler, data);
        }).catch(function(response, err) {
            data = response;
            data.isError = true;
            data.splice(err.index, 1, err.reason);
            render(Handler, data);
        });
    } else {
        render(Handler, data);
    }
});

function render(Handler, data) {
    React.render(<Handler data={data}/>, document.body);
}

In the new version, there is no Router.run I see. How can I achieve the same in 2.4.1?

Upvotes: 1

Views: 657

Answers (1)

andre
andre

Reputation: 280

You can try to use the onEnter hooks provided by the router. I will show my current setup with the React Router. Keep in mind that I declare the dependencies on the routes and not on components, but you can change that behavior to feet your needs.

Take this list of routes as example:

<Route path="/" onEnter={fetchDependencies} component={AppContainer}>
  <IndexRedirect to="/home" />
  <Route path="/home" component={StaticContainer} require={loadStaticPage} />
  <Route path="/contact" component={StaticContainer} require={loadStaticPage} />
</Route>

I add my own handler to the top router to fetch every dependency, for that you just need to set a function on the property onEnter. I also have a custom property on the routes that need some dependencies, I named that prop require and it can simply be a function that returns a promise. On your case use the component for that.

This onEnter takes a function with the following signature:

onEnter(nextState, replace, callback?)

The callback is optional and if provided the router will not render the component until the callback is called without any error. That's the behavior that you pretend.

This is how I fetch the dependencies, you can adapt this code to fit your needs

function fetchDependencies(toRoute, redirect, done) {
  const { routes, params, location } = toRoute;
  const payload = Object.assign({}, location, { params });
  const promises = routes
    .map( ({ require }) => require )
    .filter( f => typeof f === 'function' )
    .map( f => f(payload) );

  return Promise.all(promises).then( () => done() , done);
}

In your case you can use the component instead of the require property. Just change the map function to return that instead. So this line

.map( ({ require }) => require )

would be changed to something like

.map( ({ component }) => component.fetchData )

This is just an idea, the code that I've pasted is just a simplified version of the setup that I use. My current setup is tied to Redux and I tried to remove every reference about redux in that examples, that's why it may not make perfect sense. I also use isomorphic render, so my handlers are a bit more complex and I don't need the callback on the client because the redux will handle the re-render as soon the dependency is fetched.

But you get the basic idea. You need to play with onEnter hook. There you can fetch all the dependencies and call the callback function once you are done. It's just like your old setup but is organized on a slight different way.

Upvotes: 2

Related Questions