Home > Blockchain >  React Component rendering twice while executing mutiple setStates in event handler
React Component rendering twice while executing mutiple setStates in event handler

Time:04-05

first off I'm cloning Youtube for practice. I made handleSearch event handler to get videos from the search keyword.

const App = ({ youtube }) => {
  const [videos, setVideos] = useState([]);
  const [selectedVideo, setSelectedVideo] = useState(null);
  const [loading, setLoading] = useState(false);

  const handleSearch = useCallback((q) => {
    setLoading(true);
    console.log("loading -> true"); // 1

    setSelectedVideo(null);
    console.log("selectedVideo -> null"); // 2

    // 3 Component rendering

    youtube.search(q).then((videos) => {
      console.log("Before setVideos"); // 4
      setVideos(videos.map((video) => ({ ...video, id: video.id.videoId })));
      console.log("After setVideos"); //  5

      setLoading(false);
      console.log("loading -> false"); // 6
    });

    // 7 Component rendering
  }, []);

 return (
    <>
      {console.log("Component rendering")}
      ...

youtube is a class that exists in another file - it can execute fetch.

Anyway, I wrote 7 comments above about the moment when something is printed out on the console. (And my console below)

enter image description here


But I can't understand that first rendering (third printed one).

According to Dan Abramov's answer: https://stackoverflow.com/a/48610973/17356311,

The key to understanding this is that no matter how many setState() calls in how many components you do inside a React event handler, they will produce only a single re-render at the end of the event.

So if in your example we had an AJAX response handler instead of handleClick, each setState() would be processed immediately as it happens. In this case, yes, you would see an intermediate state:

setState in event handlers are batched, but not in response handlers. right?

So if the console had printed out 1 -> 2 -> 4 -> Component rendering -> 5 -> 6 -> Component rendering, I would have understood why it rendered twice. But now the sequence is strange...

Why does this component render after setting selectedVideo to null?

CodePudding user response:

First of all, you must know that “React may batch multiple setState() calls into a single update for performance”, according to React’s documentation. When your “handleSearch” callback called, it groupes all state changes in that particular function and calls them together. Also it calls a promise, for which's "then" callback, it can't know how much it should wait. So it stops grouping state changes, and rerenders. That's first rerender. Then in some time, microtask for promise ("then" callkback) is called, and in that callback two other state changes are grouped and called together, so it's second rerender.

CodePudding user response:

Based on your code, youtube.search returns a promise which doesn't block the execution of handleSearch. The .then is associating a handler for that call's eventual success. So it's basically saying, execute this asynchronous action, and when you finish (succeed), come back and execute this. Your component ends up rerendering because the handleSearch callback finished execution and you modified state: setLoading(true) and setSelectedVideo(null). Eventually, your promise call is resolved, and your callback is executed modifyng state twice again which causes the second rerender.

  • Related