Loading Route Data

In the code splitting guide, we added a function that calls import() to a route's resolve object 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 object and the value it resolves will be available in the route's response() function (as a property of the resolved object).

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'
  }
]);

Here, we will name the resolve function for fetching data "data".

The resolve.data() function will be passed an object that contains the matched route response properties, including the route params.

All resolve functions are expected to return a Promise.

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

{
  name: 'Recipe',
  path: 'recipe/:id',
  resolve: {
    data: ({ params }) => 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: {
    data: ({ params }) => 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 object 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. The data loading example shows one approach to how to do this.