3

I'm trying to understand how suspense / await work with nested routes.

The scenario I have:

  • I have a collection page that shows a list of items.
  • I have a detail page that shows the details of a single item (this page is a nested route)

What I want to achieve:

  • When the collection page is loaded, the page shows a waiting indicator while the data is being fetched (I have added a random delay to simulate this).

  • Once the data is loaded, if I click in one of the items it will navigate to the nested detail page (this page is also wrapped in a suspense / await), so we get on the left side the list and the right side the detail page for the selected character, ... here I'm showing a loading indicator.

The expected behavior:

  • Collection loading indicator is shown when the collection page is loaded.
  • Detail loading indicator is shown when the detail page is loaded.

The actual behavior:

  • Collection loading indicator is shown when the collection page is loaded.
  • Detail loading indicator is shown when the detail page is loaded, plus list page is reloaded.

How I have defined the routes:

routes.tsx

import React from "react";
import { defer, createBrowserRouter } from "react-router-dom";
import { ListPage, DetailPage } from "./pages";
import { getCharacter, getCharacterCollection } from "./api";

export const applicationRoutes = createBrowserRouter([
  {
    path: "/",
    loader: () => defer({ characterCollection: getCharacterCollection("") }),
    element: <ListPage />,
    children: [
      {
        path: ":id",
        loader: ({ params }) =>
          defer({ character: getCharacter(params.id as string) }),
        element: <DetailPage />
      }
    ]
  }
]);

How I have implemented each page:

list.tsx

import React from "react";
import { Await, Link, Outlet, useLoaderData } from "react-router-dom";
import { Character } from "../api/model";

export const ListPage = () => {
  const characters = useLoaderData() as { characterCollection: Character[] };

  return (
    <div>
      <h2>Character Collection</h2>
      <React.Suspense
        fallback={<h4 style={{ color: "green" }}> Loading characters...</h4>}
      >
        <Await resolve={characters.characterCollection}>
          {(characters) => (
            <div style={{ display: "flex", flexDirection: "row" }}>
              <div>
                <ul>
                  {characters.map((character) => (
                    <li key={character.id}>
                      <Link to={`./${character.id}`}>{character.name}</Link>
                    </li>
                  ))}
                </ul>
              </div>
              <Outlet />
            </div>
          )}
        </Await>
      </React.Suspense>
    </div>
  );
};

detail.tsx

import React from "react";
import { Await, useLoaderData } from "react-router-dom";
import { Character } from "../api/model";

export const DetailPage: React.FunctionComponent = () => {
  const data = useLoaderData() as { character: Promise<Character> };

  return (
    <div>
      <h1>Detail</h1>
      <React.Suspense
        fallback={<h4 style={{ color: "blue" }}>  Loading character...</h4>}
      ></React.Suspense>
      <Await resolve={data.character}>
        {(character) => (
          <div>
            <img src={character.image} alt={character.name} />
            <h2>{character.name}</h2>
            <p>{character.status}</p>
          </div>
        )}
      </Await>
    </div>
  );
};

Codesandbox example: https://codesandbox.io/s/vigilant-agnesi-z7tvk1

It seems like the way that I'm using to navigate just forces a full reload:

<Link to={`./${character.id}`}>{character.name}</Link>

Instead of updating only loading the nested page, is this behavior by default? or am I doing something wrong?

Braulio
  • 1,748
  • 14
  • 23

0 Answers0