Loading Route Data

In the code splitting guide, we added a function that uses dynamic import in a route's resolve function 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 respond 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 starts our data loading, it doesn't actually do anything. Instead, we should handle any loaded data with the respond function.

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

The respond 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);
  },
  respond({ 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 respond function to redirect.

You can specify the route to redirect to with redirect. 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',
  respond: ({ params }) => {
    // destructure the current location to preserve
    // query/hash values
    return {
      redirect: {
        name: 'Recipe',
        params: params,
        hash: location.hash
      }
    };
  }
}

A route's resolve and respond 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 something is happening. The async examples shows one approach to how to do this.

  1. resolve
  2. response