Loading states

Using loading states intelligently can drastically the perceived speed of your store. By loading data from the cache where possible and showing a placeholder when there is no cached data, you can make the browsing experience much smoother.

Implementing loading states with Apollo Client can be unintuitive, so we have done our best to simplify the process.

Fetch policies and loading status

First, it's important to understand how and when a piece of data is considered to be in a loading state.

The Flight framework Apollo Client for interfacing with our GraphQL API. It's definitely worth reading through the documentation for Apollo Client, as it used heavily throughout the app.

Flight handles the routing of your Pages, Categories, and Products automatically. When a user accesses a route like www.yourstore.com/black-shoes, the Flight framework automatically determines whether black-shoes is a Page, Category, or Product route. This logic is contained within the DynamicRoute component that lives in your Shop.js.

Fetch Policy

For all routes handled by DynamicRoute, Flight uses the cache-and-network fetch policy. You can read more about that here. Essentially, the default behaviour of Apollo Client is to check the cache to see if the route has already been loaded. If it has, it will immediately return the data from the cache. If there is no data in the cache, it will fetch the data from the API.

The special thing about the cache-and-network fetch policy is that even if there was data in the cache, after returning that data, Apollo Client will still make a query. We do this so that if there is new data on the server, the app can display it immediately. For example, if a product has just sold out, or if the shop admin has marked a product on sale, it's important to show that right away. If we only used the cache, it's possible that during a user's session the data will become stale.

Loading status

Whenever Apollo Client triggers a query or mutation that interacts with the API, the loading status is switched to true. Read more about this here.

When using the default cache-first Apollo Client fetch policy, the loading state is a reliable method of determining whether new data is loading. However, as described above, we use the cache-and-network policy for Products, Categories, and Pages. As a result, the loading state will become true even when data has been fetched from the cache. This means that you probably don't want to show a loading state whenever loading is true. Instead, you should check if there is data in the cache already. If there is data in the cache, you can show it immediately. If there is no data in the cache, you can show a loading state.

Null checks

The ContentPage, CategoryPage, and ProductPage receive a page, category, and product prop respectively. You should check that the prop has a value before rendering data with it.

Copy
Copied
// Return early if there is no product
if (!product)
  return (
    <ProductContainer>
      <ProductPageLoadingState />
    </ProductContainer>
  );

// Otherwise there is data, so render the actual product page
return (
  <ProductContainer>
    <ActualProductPage />
  </ProductContainer>
);

There are examples of this in Trend, for each of the components mentioned above.

LoadingLine

A simple component is available from @jetshop/ui/Loading/LoadingLine that will display a rectangular placeholder, and can be used to approximate the appearance of text.

Copy
Copied
<Playground>
  <LoadingLine widthRem={20} heightPx={24} style={{ marginBottom: "24px" }} />
  <LoadingLine
    randomizeWidthBy={10}
    widthRem={20}
    count={5}
    heightPx={12}
    style={{ marginBottom: "6px" }}
    color="silver"
  />
</Playground>

LoadingBar

If you would like to display a loading bar across the top of the screen whenever a query or mutation is loading, simply include the LoadingBar component from @jetshop/ui/Loading/LoadingBar anywhere inside the Theme in your Shop.js. To remove this behaviour, just remove the component.

To change the colour of the LoadingBar, add a colour with the key of loadingBar to your colors object in Theme.js.

For example:

Copy
Copied
const colors = {
  ...baseTheme.colors,
  blue: "#2f80ed",
  loadingBar: "green",
};

Opting out of LoadingBar display for certain queries/mutations

Sometimes you might not want to show the LoadingBar while a specific query or mutation is loading. To do this, simply add a context prop with useApolloNetworkStatus: false to your Query or Mutation:

Copy
Copied
  <Query
    context={{ useApolloNetworkStatus: false }}
    query={myQuery}
  >
    {({ loading, data }) => ()}
  </Query>

Linking to Dynamic Routes

As mentioned above, the Product, Category, and Page routes are dynamic routes, for which the Flight framework needs to figure out what kind of page should be displayed.

If your app appears to be reloading the entire page whenever you go to these routes, it's likely that you haven't linked to them properly.

When linking to any of these page types, it is important that you use the correct Link component.

ContentPageLink, ProductLink, or CategoryLink.

ProductLink

ProductLink includes product tracking. It will log a click action on the product when it is clicked.

If for some reason you don't want to use those components, here is an example of how you might link to a Content Page:

Copy
Copied
import { Link } from "react-router-dom";
<Link
  key={page.id}
  to={{
    pathname: page.primaryRoute.path,
    state: { type: "Page" },
  }}
>
  {page.name}
</Link>;
Copyright © Norce 2023. All right reserved.