React 18 Suspense minimal example 
Tags: react, javascript, redux, swr
Published: 2022-01-22 19:07:22

In the current version of React (`17.0.2` at the day of this article creation) `Suspense` is a component, that allows developers to lazy-load application parts. It accepts `fallback` property, with content to display, while the child component is lazy-loading.
```tsx
const SomePage = React.lazy(() => import("./modules/somepage"));

const App () => (
  <React.Suspense fallback={"Loading..."}>
    <SomePage />
  </React.Suspense>
);
```
However from React 18 it will be possible to use `Suspense` for data fetching. This means, that `fallback` will be displayed until component will fetch all the data needed. Or in general all events that component expect will occur. Let say we want to just display placeholder for 2 seconds:
```tsx
const Main = () => {
  useTimeout(2000);
  return <div>Component loaded</div>;
};

const App = () => (
  <Suspense fallback={"Loading..."}>
    <Main />
  </Suspense>
);

export default App;
```
As you could guess, `Loading...` will be displayed for 2 seconds and `Component loaded` afterwards.
However when I first saw the code above, I didn't understand _HOW_ did that happen.. What is that magic mechanism in the `useTimeout` hook? In short it has to:
1. Stop component code execution.
2. Let `Suspense` know that the component isn't yet ready
3. Notify `Suspence` know when it should re-attempt with rendering component.

To stop code execution you need use `throw` statement. In order to make `Suspense` know it's expected, the value thrown need to be a `Promise`. `Suspense` will catch this promise and subscribe to it, to re-attempt rendering.
Please note: the code bellow is just for a demo purpose:
```tsx
let fullfilled = false;
let promise = null;

const useTimeout = (ms: number) => {
  // check if timeout already occurred.
  if (!fullfilled) {
    // if promise doesn't exist create and throw it.
    throw promise ||= new Promise((res) => {
      setTimeout(() => {
        // on next attempt consider timeout completed.
        fullfilled = true;
        // resolve promise (will ask react to re-render).
        res();
      }, ms);
    });
  }
};
```
_(Confused about `||=` ? Check [this doc](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_OR_assignment))_

It turns out that suspense uses quite simple mechanisms, but there's a hard part. You might ask why `fullfilled` and `promise` couldn't be stored in a ref, so the hook would be reusable:
```tsx
const fullfilled = useRef(false);
```
It turns out, that while component is not loaded, hooks can't be really used. Component will be unmounted / mounted on every render attempt before the render will complete without throwing promises. Hence to figure out, if this component has actually started data loading process, we should rely on a globally available cache. (In our simplified case it's just `fullfilled` variable). Of course in a real-world example such simple approach wouldn't work (this hook works only one time).

This is why it's advised to use good libraries that supports suspense (like [swr](https://swr.vercel.app/)).

[Full code](https://codesandbox.io/s/suspense-timeout-example-4g7rh) of the example above.

👋