hane Smitter
hane Smitter

Reputation: 880

How does react suspense determine if a component is ready to render

I am using <React.Suspense> for data fetching like in this blog post.
From the react docs

Suspense lets your components wait for something before they can render.

And even better explained in this docs

React.Suspense lets you specify the loading indicator in case some components in the tree below it are not yet ready to render

I have understood that a loading indicator specified as fallback prop in <React.Suspense> is displayed if a component is not ready to render.

However I am not understanding how React is checking if a component is ready to render or not. According to this docs

As more data streams in, React will retry rendering, and each time it might be able to progress “deeper”.

Does this mean React also re-renders when data streams in from a network request?
And is <React.Suspense> constantly checking if a component is ready to render or not?

Upvotes: 14

Views: 4385

Answers (1)

Thor-x86_128
Thor-x86_128

Reputation: 361

React.Suspense relies on Promise with a special wrapper. Practically, we use React.lazy as the wrapper. If you interested to understand how the wrapper works, see the snippet below.

// Boilerplate for the snippet, usually live in index.js
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);


// Main Component
function App() {
  return (
    <React.Suspense fallback={<LoadingPage />}>
      <RealPage />
    </React.Suspense>
  );
}

// Promise need to be monitored with a wrapper
function wrapPromise(promise) {
  let status = "pending";
  let result;
  let suspender = promise.then(
    (r) => {
      status = "success";
      result = r;
    },
    (e) => {
      status = "error";
      result = e;
    }
  );
  return {
    read() {
      if (status === "pending") {
        throw suspender;
      } else if (status === "error") {
        throw result;
      } else if (status === "success") {
        return result;
      }
    },
  };
}

// This is an example of a delayed process that
// uses `promise` to mock resource fetch
function delayedProcessExample() {
  // Wait for 3 seconds
  let promise = new Promise((resolve) => setTimeout(resolve, 3000))
    // Return an example result
    .then(() => "Hello, World!");

  return promise;
}

// The delayed process should be executed somewhere.
// If you put it in a component, then the App going to
// request indefinitely
const exampleResource = wrapPromise(delayedProcessExample());

// This is how you access the resource
function RealPage() {
  const exampleData = exampleResource.read();
  return <div>{exampleData}</div>;
}

// This is the fallback component
function LoadingPage() {
  return <div>Loading... Please Wait</div>;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
<div id="root"></div>

Notice the wrapper throws suspender promise. That expression tells the React.Suspend there is a promise which need to be waited. Thus, basically React.Suspend depends on Promise as an Exception to wait the data.

Sources:

Upvotes: 6

Related Questions