Loading Route Data

In the code splitting guide, we added a function that calls import() to a route's resolve function in order to dynamically load modules. We can do the same thing for other data.

resolve

An async function (with any name you want it to have) can be added to the resolve function and the value it resolves will be available in the route's response function.

When the Recipe route matches, we want to fetch data for that specific recipe (using the id param from the path).

const routes = prepareRoutes([
  {
    name: 'Recipe',
    path: 'recipe/:id'
  }
]);

The resolve function is passed an object that contains the matched route response properties, including the route params.

Now, when we navigate to /recipe/cookies, the resolve function will call the fake API function to load the "cookies" recipe. The function will resolve with the loaded data.

{
  name: 'Recipe',
  path: 'recipe/:id',
  resolve({ params }) {
    return fakeAPI.getRecipe(params.id);
  }
}

response

While resolve.data() starts our data loading, it doesn't actually do anything. Instead, we should handle any loaded data with the response function.

The response and resolve.data() are separate because while a route is resolving, the user may navigate again, which overrides the current navigation. We cannot cancel the resolve.data() function for the current navigation, so if it performs any side effects, our application is stuck with them. To avoid this, the response function is not called until we know that the current navigation will complete.

The response function will receive an object with a number of properties. These are covered in in the Routes and Responses guide, but the only one we care about right now is resolved.

{
  name: 'Recipe',
  path: 'recipe/:id',
  resolve({ params }) {
    return fakeAPI.getRecipe(params.id);
  },
  response({ resolved }) {
    return {
      body: Recipe,
      data: resolved.data
    }
  }
}

If at some point in time we decide that we want to change our URI pathname structure, we can also use the response function to redirect.

You can specify the route to redirect to with redirectTo. This takes the name of the route to redirect to, params if the route (or ancestor routes) have route params. hash, query, and state can also be provided.

After Curi emits the response, it will also automatically redirect to the new location!

{
  name: 'Old Recipe',
  path: 'r/:id',
  response: ({ params }) => {
    // destructure the current location to preserve
    // query/hash values
    return {
      redirectTo: {
        name: 'Recipe',
        params: params,
        hash: location.hash
      }
    };
  }
}

A route's resolve and response functions offer a convenient way to do data loading prior to actually rendering the route, but please remember that your application will not be re-rendering until after the fetching has resolved. If you have a long running load function, you may wish to implement some sort of loading display to let the user know that somethign is happening. The data loading example shows one approach to how to do this.