0

Assume fetchUserProfile is defined elsewhere. Is there anything wrong with Suspense usage?

import { Suspense, useState, useEffect } from 'react';

const SuspensefulUserProfile = ({ userId }) => {
  const [data, setData] = useState({});
  useEffect(() => {
    fetchUserProfile(userId).then((profile) => setData(profile));
  }, [userId, setData])
  return (
    <Suspense>
      <UserProfile data={data} />
    </Suspense>
  );
};
const UserProfile = ({ data }) => {
  return (
    <>
      <h1>{data.name}</h1>
      <h2>{data.email}</h2>
    </>
  );
};
const UserProfileList = () => (
  <>
    <SuspensefulUserProfile userId={1} />
    <SuspensefulUserProfile userId={2} />
    <SuspensefulUserProfile userId={3} />
  </>
);
wangdev87
  • 8,611
  • 3
  • 8
  • 31
  • What are you expecting this `` to do? It's not going to interact with fetchUserProfile at all, if that's what you mean. – Nicholas Tower Jan 21 '21 at 01:18
  • I'm quite new to Suspense, would you please correct what I did in my suspense usage? – wangdev87 Jan 21 '21 at 01:19
  • What is your goal? Once I know what you're trying to do I can give you advice on how to do that, and it's quite possible that advice might be to not use Suspense at all. Are you trying to load data and show a placeholder while the loading is going on? – Nicholas Tower Jan 21 '21 at 01:25
  • yes, wait for load and show spinner, but using suspense – wangdev87 Jan 21 '21 at 01:28
  • Why do you want to use suspense for this? I strongly recommend you do not use suspense for data loading unless you really know what you're doing. – Nicholas Tower Jan 21 '21 at 01:28
  • sorry to not adding a details, but Suspese is a must on my case according to the requirement. do you have any idea to refactor above codebase using suspense? – wangdev87 Jan 21 '21 at 01:30
  • at first suspense is used for lazy loading. it seems there is some sort of work on suspense for data fetching https://reactjs.org/blog/2018/11/27/react-16-roadmap.html#react-16x-mid-2019-the-one-with-suspense-for-data-fetching – buzatto Jan 21 '21 at 01:31

1 Answers1

1

As discussed in the comments, i recommend you do not use suspense for this. Suspense for data loading is an experimental feature and you can tie yourself in knots if you don't understand exactly what's going on.

The standard way to implement something like this is pretty much as you have it now, except delete the suspense, and check for the empty state. For example:

const DataLoader = ({ userId }) => {
  const [data, setData] = useState(null);
  useEffect(() => {
    fetchUserProfile(userId).then((profile) => setData(profile));
  }, [userId, setData])

  if (data === null) {
    return <div>Loading...</div>
  }
  return (
    <UserProfile data={data} />
  );
};

If you want to use suspense, you need to invert the way you are doing things. Suspense needs to be outside of the component that is doing the data loading, and then you need to throw a promise in order to tell suspense that loading is in progress:

const OuterComponent = () => {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <DataLoader />
    </Suspense>
  );
}

let data;

const DataLoader = () => {
  if (!data) {
    throw fetchUserProfile(userId)
      .then((profile) => { data = profile });
  }

  return <UserProfile data={data} />
}

Note that the data needs to be outside of the component, because once the promise resolves, suspense is going to try to render the component again and your data must be synchronously available, or else you'll throw another promise and enter an infinite loop.

Nicholas Tower
  • 72,740
  • 7
  • 86
  • 98