skip to Main Content

let’s look this code:
app.tsx

import "./App.css";
import useHome from './hooks/useHome';

function App() {
  useHome();
  return <></>;
}

export default App;

hooks/useHome.ts

import { useEffect, useState } from "react";

const query = (): Promise<number> =>
  new Promise((resolve) =>
    setTimeout(() => {
      resolve(1);
    }, 1000),
  );

const useHome = () => {
  const [pageStatus, setPageStatus] = useState<string>("loading");
  const [data, setData] = useState<number | null>(null);
  const [deps, setDeps] = useState(data);

  const queryData = async () => {
    setPageStatus("loading");
    try {
      const pageData = await query();
      setData(pageData); // cause second render
      setPageStatus("normal"); // cause third render
    } catch (e) {
      setPageStatus("error");
    }
  };

  useEffect(() => {
    queryData(); 
  }, []);

  useEffect(() => {
    setDeps(data); // cause third render 
  }, [data]);

  console.log("@render"); // total 3 times

  return {
    deps,
    data,
    pageStatus,
  };
};

export default useHome;

why ‘@render’ only console.log 3 times?
I expected it render 4 times;
render first at init,
render second because of setData
render third because of setPageStatues ( async function environment here )
render forth because of setDeps
but I find it only console 3 times…
Is react merged the setPageStatues and setDeps ?

3

Answers


  1. The console log will trigger each time your component is rerendered. (i.e each time there is a state change in the component)

    Additionally initially react will render your component twice in development mode but it will only do it locally and not in production builds.

    Login or Signup to reply.
  2. setData(pageData); // cause second render
    setPageStatus("normal"); // cause third render
    

    Your assumption here is mistaken. These don’t each cause a re-render. These queue/batch their respective state updates. When the operation is complete, state updates are performed and the component re-renders.

    Login or Signup to reply.
  3. The answer to your question is: React’s batch update mechanism

    1. Initial Render: When your component App mounts, it triggers the useHome hook. At this point, @render logs once.
    2. Triggering queryData via useEffect:
      Inside queryData, you first set the pageStatus to "loading", which queues a state update.(not doing it immediately, just queue it)
      You then await query() which resolves after 1 second.
      After query() resolves, setData is called, queuing another state update.
      Immediately after setting data, you set pageStatus to "normal", queuing yet another state update.
      So here in such scenarios, React intelligently batches these updates into a single render cycle. Therefore, these changes result in just one render instead of separate renders.
    3. the final (the 3rd render) caused by the setDeps as you already mentioned.
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search