React18 microtask batch processing problem
P粉506963842
P粉506963842 2023-09-07 22:30:24
0
1
504

useEffect(() => {
  console.log("render");
});

const handleClick = () => {
  setC1((c) => c + 1);

  Promise.resolve().then(() => {
    setC1((c) => c + 1);
  });
};

const handleClick2 = () => {
  Promise.resolve().then(() => {
    setC1((c) => c + 1);
  });

  setC1((c) => c + 1);
};

In the React18 version, why does clicking the handleClick method cause two renderings, while clicking the handleClick2 method only causes one rendering?

I want the output of both methods to be the same. Can anyone tell me why they are different?

P粉506963842
P粉506963842

reply all(1)
P粉642920522

I will explain how these call sequences differ and how the observed behavior is possible.

I can’t tell you exactly how React updates status in batches internally, I just assume that React has complex optimizations that are irrelevant to the developer using React and require a deep understanding of React internals and maybe even changing from one version to another. (Please feel free to correct me.)

the difference

Promise.resolve() Arranges a new microtask, which is actually equivalent to window.queueMicrotask().

setState Function (possibly) will also schedule a new microtask, Therefore their callbacks (Promise and setState) are called in the same execution phase.

The difference between these two variants is

  • In handleClickA, the setState2 hook is called between the two updater functions, while
  • In handleClickB, the two updater functions will be called directly in sequence.

Sample code

I rewrote your code slightly to better illustrate the calling sequence:

const setState1 = setState;     
const setState2 = setState;
const update1 = updaterFunction; // c => c + 1
const update2 = updaterFunction; // c => c + 1

const handleClickA = () => {          
                                  // Scheduled functions:
    setState1( update1 );         // 1. --> [ update1 ]
    
    queueMicrotask(() => {        // 2. --> [ update1, setState2 ]
        setState2( update2 );     // 4. --> [ update2 ]
    });

    // update1();                 // 3. --> [ setState2 ]
    // setState2( update2 );      // 4. --> [ update2 ]
    // update2();                 // 5. --> []
};

const handleClickB = () => {
                                  // Scheduled functions:    
    queueMicrotask(() => {        // 1. --> [ setState2 ]
        setState2( update2 );     // 3. --> [ update2 ]
    });

    setState1( update1 );         // 2. --> [ setState2, update1 ]
    
    // setState2( update2 );      // 3. --> [ update1, update2 ]
    // update1();                 // 4. --> [ update2 ]
    // update2();                 // 5. --> []
};

Call sequence description

Here I explain the calling sequence.

(FIFO >):

handleClickA

// 0. --> []
- schedule update1 (setState1())  // 1. --> [ update1 ]
- schedule setState2              // 2. --> [ update1, setState2 ]
- invoke update1()                // 3. --> [ setState2 ]
- schedule update2 (setState2())  // 4. --> [ update2 ]
- invoke update2()                // 5. --> []

handleClickB

// 0. --> []
schedule setState2              // 1. --> [ setState2 ]
schedule update1 (setState1())  // 2. --> [ setState2, update1 ]
schedule update2 (setState2())  // 3. --> [ update1, update2 ]
invoke update1()                // 4. --> [ update2 ]
invoke update2()                // 5. --> []

Personal interpretation

I assume React attempts to batch all updater functions currently queued.

i.e. whenever only the updater function is called, try batching them together and only update the final state once.

However, if a new setState function is called, React may complete the current update loop and start a new render before calling the next updater periodic function. 代码>

I can only guess why this is done

    Because the new
  • setState might somehow break the batch, or
  • If new
  • setState calls are made recursively, the next render will be delayed too much, or
  • React people are still figuring out the best optimization strategies and their trade-offs.
  • (...or this is a bug.)
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template