React 18 Suspense fetch data from a headless CMS
Tags: javascript, react, webdev, tutorial
Published: 2022-08-19 15:46:00

To deliver a great website user experience, we need to optimize the first initial page load time and the page's responsiveness to interaction. The faster your page responds to user input – the better.

[React 18](https://www.youtube.com/watch?v=FZ0cG47msEk&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa) was designed to help improve interactivity with features like [selective hydration](https://github.com/reactwg/react-18/discussions/130) with `Suspense` to make hydration non-blocking and provide us with more transparency about how our [architectural](https://github.com/reactwg/react-18/discussions/37) choices will affect our app's UX and performance. React 18 makes major performance improvements and adds support for `Suspense` on server-side rendering (SSR) allowing serving parts of an app asynchronously possible, you can wrap a slow part of your app within the Suspense component, telling React to delay the loading of the slow component. 

![React-Suspense.jpeg](https://imgix.cosmicjs.com/ba220590-1e67-11ed-a5bd-ef15a2e468ca-React-Suspense.jpeg)

Server-side rendering lets you generate HTML from React components on the server, and send that HTML to your users. SSR lets your users see the page’s content before your JavaScript bundle loads and runs, after which the JavaScript code loads and merges with the HTML, attaching event handlers  - which is **hydration**. Unlike traditional HTML streaming, it doesn’t have to happen in the top-down order.  

![React-18-Suspense.jpeg](https://imgix.cosmicjs.com/ba1fbba0-1e67-11ed-a5bd-ef15a2e468ca-React-18-Suspense.jpeg)
With `Suspense`, you can tell React to send HTML for other components first along with the HTML for the placeholder, like a loading spinner. It significantly improves the user experience and user-perceived latency.

**There are two major SSR features in [React 18 unlocked by Suspense](https://github.com/reactwg/react-18/discussions/37):** 
- Streaming HTML on the server. 
- Selective Hydration on the client.

Let’s explore [React Data fetching approaches](https://17.reactjs.org/docs/concurrent-mode-suspense.html#what-suspense-is-not)  with  `useEffect` and `Suspense` try to compare backend data fetching practical solutions, in our case we choose a fast and intuitive headless CMS [Cosmic](https://www.cosmicjs.com/). Our code examples you can check by a link [StackBlitz](https://stackblitz.com/edit/react-fetch-data-cms?file=src%2Findex.js,src%2FApp.js). 
{% embed https://stackblitz.com/edit/react-fetch-data-cms?file=src%2FApp.js %}

### Integration Cosmic Headless CMS

For fetching data we use [Cosmic headless CMS](https://www.cosmicjs.com/headless-cms) is a back-end-only content management system (CMS), which is built from the ground up a content repository that makes content accessible. To integrate and get values from Cosmic, we need to install the Cosmic module in your project.

```bash
npm i cosmicjs
# or
yarn add cosmicjs
```

Then create a [free Cosmic account](https://app.cosmicjs.com/signup) and go to [Cosmic Dashboard](https://app.cosmicjs.com/projects) `Your Bucket > Settings > API Access` and find your Bucket slug and API read key and add them to creating Cosmic fetch function `fetchDataByType` request to your Cosmic bucket and fetch created Categories content by [Cosmic query](https://www.cosmicjs.com/changelog/introducing-advanced-queries) type `categories`.

```jsx
// cosmic.js
import Cosmic from 'cosmicjs';

const bucket = Cosmic().bucket({
  slug: 'your_cosmic_slug',
  read_key: 'your_cosmic_read_key',
});

export async function fetchDataByType(objectType = 'categories') {
  const params = {
    query: {
      type: objectType,
    },
    props: 'title,slug,id,metadata',
    sort: '-created_at',
  };

  try {
    const data = await bucket.getObjects(params);
    return data.objects;
  } catch (error) {
    return { error };
  }
}

```
Cosmic also provide powerful content modeling features that let you create any kind of content super-fast and multi-channel publishing, for realizing create once and publish everywhere.

## [**Data fetching approaches**](https://17.reactjs.org/docs/concurrent-mode-suspense.html#what-suspense-is-not)

### Fetch-on-render

**Fetch-on-render approach** the network request is triggered in the component itself after mounting, the request isn’t triggered until the component renders. If you don't write a cleanup function that ignores stale responses, you'll notice a **race condition** (in React) bug when two slightly different requests for data have been made, and the application displays a different result depending on which request completes first. In fact on React 18, if you enable StrictMode in your application, in development mode you will find out that using useEffect will be invoked twice, because now React will mount your component, dismount, and then mount it again, to check if your code is working properly.

Let's fix a data fetching race condition by taking advantage of the `useEffect` clean-up function. If we're okay with making several requests, but only rendering the last result, we can use a boolean flag `isMount`:

```jsx
// FetchWithUseEffect/App.js
import React, { useEffect, useState } from 'react';
import Category from './components/Category';
import { fetchDataByType } from './cosmic.js';

const App = () => {
  const [categories, setCategories] = useState([]);

  const getCategories = async () => {
    const result = await fetchDataByType('categories');
    if (result.length) {
      setCategories(result);
    }
  };

  useEffect(() => {
    let isMount = true;

    if (isMount) {
      getCategories();
    }

    //useEffect clean-up function
    return () => {
      isMount = false;
    };
  }, []);

  return (
    <div className={cn('container', styles.container)}>
     <div className={styles.sidebar}>
      <div className={styles.collections}>
       {categories?.map((category) => (
         <Category key={category.id} info={category} />
        ))}
       </div>
      </div>
     </div>
  );
};

export default App;

```

> Additionally, if a component renders multiple times (as they typically do), the previous effect is cleaned up before executing the next effect.
> 

In this case, we still have a race condition in the sense that multiple requests to Cosmic will be in-flight, but only the results from the last one will be used.

![FetchWithuseEffect](https://imgix.cosmicjs.com/25cd4020-1f26-11ed-a5bd-ef15a2e468ca-App.js-9.png)

![useEffect](https://imgix.cosmicjs.com/355ba030-1e69-11ed-a5bd-ef15a2e468ca-Screen-Shot-2022-08-15-at-3.54.56-PM.png)

Also as [Dan Abramov](https://www.reddit.com/r/reactjs/comments/vi6q6f/comment/iddrjue/) explains, **Fetch-on-render** provides slow navigation between screens. If you have **parent and child components** both doing fetching in `useEffects`, then the child component can't even start fetching until the parent component finishes fetching. These types of performance problems are very common in single-page apps and cause a lot more slowness than "excessive re-rendering" and if we have a complex application with multiple parallel requests, we would see different parts of the application load in random order. The more natural behavior for an application is to render things from top to bottom.


### Render-as-you-fetch

**Render-as-you-fetch approach** lets us begin rendering our component immediately after triggering the network request and we start rendering pretty much immediately after kicking off the network request. 

### Suspense for Data Fetching

**With Suspense, we don’t wait for the response to come back before we start rendering and reduce the Total Blocking Time (TBT) of our example from 106ms to 56ms.**

![Suspense](https://imgix.cosmicjs.com/129e0c10-1fb6-11ed-a5bd-ef15a2e468ca-suspense-some-loading.webp)

The React core team set of concurrent features to make data fetching in React easier. [Suspense](https://17.reactjs.org/docs/concurrent-mode-suspense.html)  is among these, and it aims to simplify managing loading states in React components.  It’s a feature for managing asynchronous operations in a React app and lets you use `<Suspense>` to declaratively “wait” for anything else, including data, and no longer have to wait for it all the JavaScript to load to start hydrating parts of the page. 

First, we trigger the network request before rendering any components on line one. In the main `App` component, we wrap both  `Category` and `Cards`, `Main` components in separate `Suspense` components with their fallbacks.

![ReactDataFetchSuspense](https://imgix.cosmicjs.com/5185b990-1f26-11ed-a5bd-ef15a2e468ca-App.js-10.png)

![SuspenseCall](https://imgix.cosmicjs.com/f8a67010-1e69-11ed-a5bd-ef15a2e468ca-Screen-Shot-2022-08-15-at-3.56.50-PM.png)

When `App` mounts for the first time, it tries to render `Category`, which triggers the `resourseCategories.read()` line. If the data isn’t ready yet (i.e., the request hasn’t been resolved), it is communicated back to Suspense, which then renders `<p>Loading…</p>`. The same thing happens for `Cards` and `Main`

```jsx
// App.js
import React, { Suspense } from 'react';

const App = () => {
  return (
    <main>
      <Suspense fallback={<p>Loading.....</p>}>
        <Cards />
      </Suspense>
      <div>
        <Suspense fallback={<p>Loading.....</p>}>
          <Category />
        </Suspense>
      </div>
    </main>
  );
};

export default App;
```

`Suspense` it is not a new interface to fetch data, as that job is still delegated to libraries like fetch or Axios, and `Suspense` real job is to just say "show this code while is loading, and show that when it's done", nothing more than that.

### **Wrap your fetching logic** `wrapPromise.js`

We also need wrap fetching logic, to throw an exception when our components are loading the data or it failed, but then simply return the response once the `Promise` is resolved successfully and if it is still pending, it throws back the Promise.

```jsx
// wrapPromise.js
//Wraps a promise so it can be used with React Suspense

function wrapPromise(promise) {
  let status = 'pending';
  let response;

  const suspender = promise.then(
    res => {
      status = 'success';
      response = res.objects;
    },
    err => {
      status = 'error';
      response = err;
    },
  );

  const handler = {
    pending: () => {
      throw suspender;
    },
    error: () => {
      throw response;
    },
    default: () => response,
  };

  const read = () => {
    const result = handler[status] ? handler[status]() :
    handler.default();
    return result;
  };

  return { read };
}

export default wrapPromise;

```

At the end of the `wrapPromise` function will check our promise's state, then return an object containing the `read` function as a method, and this is what our React components will interact with to retrieve the value of the Promise.

Now we'll need to wrap the Cosmic call functions to `wrapPromise`:

```jsx
// cosmic.js

export function fetchDataByType(objectType = 'categories') {
  const params = {
    query: {
      type: objectType,
    },
    props: 'title,slug,id,metadata',
    sort: '-created_at',
  };

  const data = bucket.getObjects(params);
  return wrapPromise(data);
}
```

The above is just an abstraction for Cosmic fetching functions with `Suspense` and fetch one time.

### Read the data in the component

Once everything is wrapped up on the fetching side of things, we want to use it in our component. So what is happening when we call the component, the `read()` function will start to throw exceptions until it's fully resolved, and when that happens it will continue with the rest of the code, in our case to render it.

```jsx
//components/Category
import React from 'react';
import { fetchDataByType } from '../../cosmic.js';
import styles from '../../styles/Collection.module.scss';

const resourseCategories = fetchDataByType();

const Category = () => {
  const categories = resourseCategories.read();

  const renderCategories = categories?.map((info) => (
    <div key={info?.id} className={styles.user}>
      <div className={styles.avatar}>
        <img
          className={styles.image}
          src={info?.metadata?.image?.imgix_url}
          alt="Avatar"
        />
      </div>
      <div className={styles.description}>
        <div className={styles.name}>{info?.metadata?.title}</div>
        <div
          className={styles.money}
          dangerouslySetInnerHTML={{ __html: info?.content }}
        />
      </div>
    </div>
  ));

  return <div className={styles.collections}>{renderCategories}</div>;
};

export default Category;

```

### **The parent component**

`Suspense` gives React access to pending states in our applications and thats why React knows that a network call is happening, this allows us to render a fallback component declaratively while waiting. 

```jsx
// App.js
import React, { Suspense } from 'react';
import Cards from './components/Cards';
import Category from './components/Category';
import Main from './components/Main';
import styles from './styles/Collection.module.scss';

const App = () => {
  return (
    <div className={styles.wrapper}>
      <div className={cn('section-pb', styles.section)}>
        <div className={cn('container', styles.container)}>
          <div className={styles.row}>
            <Suspense fallback={<p>Loading.....</p>}>
              <Main />
              <Cards />
            </Suspense>
          </div>
          <div className={styles.sidebar}>
            <div className={styles.info}>
              Collections
              <span className={styles.smile} role="img" aria-label="fire">
                🔥
              </span>
            </div>
            <Suspense fallback={<p>Loading.....</p>}>
              <Category />
            </Suspense>
          </div>
        </div>
      </div>
    </div>
  );
};

export default App;
```

## **Conclusion**

Now, with `Suspense`, you can break your app into small, stand-alone units that can be rendered on their own without the rest of the app, allowing content to be available to your user even much faster than before. We explored the various data fetching approaches for comparison.

Try it in your own project and give us your feedback. You can get started with [Cosmic](https://www.cosmicjs.com/) for a quick CMS to test data fetching with `Suspense` for websites and apps.