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.
// 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.
<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:
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:
<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:
import { Link } from "react-router-dom";
<Link
key={page.id}
to={{
pathname: page.primaryRoute.path,
state: { type: "Page" },
}}
>
{page.name}
</Link>;