Search code examples
javascriptreactjsreact-suspense

How to return a promise from a Provider in react


I have a provider that does some work when it loads (one time). It adds a script tag for a map then when that script is loaded it initializes the map.

I want to the children to be able to use suspense. I'd like to return from that provider a promise which either throws if not resolved (for suspense) or it returns the map.

I can't figure out how to do this inside my component. Of note, I'm using nextJS so I have to make sure I'm on the client side too.

I made a code sandbox to simplify it:

function App() {
  return (
    <MyProvider>
      <Suspense fallback={<h1>Loading</h1>}>
        <Child />
      </Suspense>
    </MyProvider>
  );
}

Child consumes the map and displays it, in this case it's just text:

const Child = () => {
  const { data } = useContext(context);

  const result = data.read();

  return <div>Result: {result}</div>;
};

Here is the provider:

const MyProvider = ({ children }) => {
  useEffect(() => {
    // I do asynchronous work here which adds a script tag and when
    // it is fully loaded initializes a map. How do I pass this promise
    // down to children in a way that will work with suspense?
    const data = wrapPromise(
      new Promise(resolve => {
        setTimeout(() => {
          console.log("fetched data");
          resolve({
            name: "Ringo Starr"
          });
        }, 1000);
      })
    );
  }, []);
  return <context.Provider value={{ data }}>{children}</context.Provider>;
};

Solution

  • You want to create the promise once, and memoize it:

    const data = useMemo(() => new Promise(resolve => {
      setTimeout(resolve, 1000, { name: "Ringo Starr" });
    }), []);