skip to Main Content

This is an example given in the react docs for fetching data with useEffect.

export default function Page() {
  const [person, setPerson] = useState('Alice');
  const [bio, setBio] = useState(null);

  useEffect(() => {
    let ignore = false;
    setBio(null);
    fetchBio(person).then(result => {
      if (!ignore) {
        setBio(result);
      }
    });
    return () => {
      ignore = true;
    };
  }, [person]);

When the state person changes, the component will re-render, with a new person name and then useEffect runs and set the state bio with the fetched data. Now the screen shows new person name and bio. But before useEffect runs, and as the component re-render with the new person name, screen will have new person name but old person’s bio for a short time. Is my understanding correct, if so is there any way to change this behavior

I am a new to react and if my question is dumb, please correct me.

2

Answers


  1. export default function Page() {
      const [person, setPerson] = useState('Alice');
      const [bio, setBio] = useState(null);
      const [isLoading, setIsLoading] = useState(false)
    
      useEffect(() => {
        let ignore = false;
        setIsLoading(true)
        fetchBio(person)
          .then(result => {
            if (!ignore) {
              setBio(result);
            }
            setIsLoading(false)
          })
          .catch(e => {
            console.error(e)
            setIsLoading(false)
          });
        return () => {
          ignore = true;
        };
      }, [person]);
      
      
      return (
        <>
          <h1>`Profile of ${person}`</h1>
          {isLoading ? <div>Loading...</div> : <>render(bio)</>}
        </>
      )
     }

    Looks like you need a variable to control the status of getting data. I’m using isLoading for example. With it, you can decide what to show when waiting for the new bio, here is the div with loading… text

    Login or Signup to reply.
  2. If you use two separate state that are updated independently, then yes, there can be occasional times when the two are not in "agreement", e.g. the current bio state may not be that of the current person state.

    Using a useEffect hook like this might be considered a bit of an anti-pattern though. If the intent is for the person and bio state to always be updated together then do the bio fetch at the same time that person will be updated, and enqueue the state updates to both when the UI has both new datas.

    Basic Example:

    export default function Page() {
      const [person, setPerson] = useState('Alice');
      const [bio, setBio] = useState(null);
    
      const fetchData = async (person) => {
        const bio = await fetchBio(person);
        setPerson(person);
        setBio(bio);
      };
    
      ...
    
      <button type="button" onClick={() => fetchData("Bob")>
        Get Bob's data
      </button>
    
    }
    

    The above is the basic gist, but you should "fancy it up" with loading state/indicators, cancelling in-flight network requests (Using "isLoaded" flags in hooks became a bit of a React anti-pattern over the past couple years) if Page unmounts, etc in your actual code if that is something of a concern.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search