0

I'm grabbing user data from an API. The API call takes a while. But even when the frontend gets the response, React never finishes rendering the component.

I can replicate this behavior with setTimeout. A small timeout like 7ms will work, but if I use a larger timeout like 700ms <h1>Text rendered!</h1> doesn't display.

  useEffect(() => {
      setTimeout(() => setUsers({ data: ["user1", "user2", "user3"] }), 700);
  }, []);

In the debugger I can see that the flow seems to be working

  1. The frontend receives the response, and calls setUser.
  2. This triggers the second useEffect callback which updates users.
  3. This re-renders the component, and users && users.updated is true, but then <h1>Text rendered!</h1> doesn't render for some reason.

Oddly though, on codesandbox, I get completely different behavior: <h1>Text rendered!</h1> never renders, even if I use a small timeout like 1ms, or even if I just use setUsers with no setTimeout. And on my local environment, sometimes 700ms does work if I step through it in the debugger slowly but I can't always replicate this.

What's going on? How can I get {users && users.updated && <h1>Text rendered!</h1>} to render properly on longer asynchronous calls?

Full code:

import React, { useEffect, useState } from "react";
export default function App() {
  const [users, setUsers] = useState(null);

  // 1. Get Data from API
  useEffect(() => {
      //----replicating asyncronous API call----
      //"Text rendered!" doesn't render if the timeout is large (700ms)
      //but does render if timeout is small (2ms)
      //(On codesandbox "Text rendered!" never renders, even if you don't use a timeout just use setUsers)
     
      setTimeout(() => setUsers({ data: ["user1", "user2", "user3"] }), 2);
  }, []);

  // 2. Once Data is Receive from API, Transform It
  useEffect(() => {
    if (users) {
      const usersCopy = users;

      usersCopy.updated = true;

      setUsers(usersCopy);
    }
  }, [users]);

  // 3. After the Data is Transformed, Render "Text Rendered!" 
  return (
    <>
      Render text below:
      {users && users.updated && <h1>Text rendered!</h1>}
    </>
  );
}

Dashiell Rose Bark-Huss
  • 2,173
  • 3
  • 28
  • 48
  • 1
    The main problem is this: `const usersCopy = users;` You're supposed to do `const usersCopy = { ...users };` so it's a new object, otherwise React cannot detect the change. Also, setting users inside a useEffect with a [users] dependency creates an infinite loop. Doc ref: https://reactjs.org/docs/state-and-lifecycle.html#do-not-modify-state-directly –  Jan 06 '22 at 16:24
  • 1
    Thanks! Changing to `if (users && !users.updated) {const usersCopy = { ...users };` solved it! – Dashiell Rose Bark-Huss Jan 06 '22 at 16:29

0 Answers0