Skip to content

Bug: Updates within microtasks are sometimes not batch #24365

Closed as not planned
Closed as not planned
@zh-lx

Description

@zh-lx

React version: 18.0.0

Steps To Reproduce

Normal example

The following example is normal. The value of count is incremented by 1 when click the button.

The update of Code Snippet ACode Snippet B and Code Snippet C will be all batch. Because by the time the code executes to Code Snippet A and Code Snippet C, Code Snippet B has already been added to the microtask queue. The flushSyncCallbacks are executed in scheduleMicrotask callback function, therefore, Code Snippet B is executed first and then flushSyncCallbacks is executed.

import { useEffect, useState, useRef } from "react";
export default function App() {
  const [count, setCount] = useState(0);
  const countRef = useRef(-1);

  const handleClickButton = () => {
    // No.B
    Promise.resolve().then(() => {
      setCount(countRef.current + 1);
    });

    // No.A
    setCount(countRef.current + 1);

    // No.C
    setCount(countRef.current + 1);

    // Code Snippet A、Code Snippet B and Code Snippet C are all batch processed
  };

  useEffect(() => {
    countRef.current = count;
  }, [count]);

  return (
    <div>
      <p>count is: {count}</p>
      {/* count will be incremented by 1 when click the button */} 
      <button onClick={handleClickButton}>增加</button>
    </div>
  );
}

link to code example: https://codesandbox.io/s/batch-update-in-native-xpwz33-forked-c4pii4?file=/src/App.js:0-581

Abnormal examples

The following example is abnormal. The value of count is incremented by 2 when click the button.

The update of Code Snippet A and Code Snippet C will be batch, but the update ofCode Snippet B won't be batch.
When the code executes to Code Snippet C, Code Snippet B has not been batch, although Code Snippet B has been added to the microtask queue.

I think all updates added to the microtask queue should be batched when the code executes to the last update on the synchronous execution context.

import { useEffect, useState, useRef } from "react";
export default function App() {
  const [count, setCount] = useState(0);
  const countRef = useRef(-1);

  const handleClickButton = () => {
    // Code Snippet A
    setCount(countRef.current + 1);

    
    Promise.resolve().then(() => {
      // Code Snippet B
      setCount(countRef.current + 1);
    });

    // Code Snippet C
    setCount(countRef.current + 1);

    // Code Snippet A and Code Snippet C will be batch, but Code Snippet B won't be batch
  };

  useEffect(() => {
    countRef.current = count;
  }, [count]);

  return (
    <div>
      {/* count will be incremented by 2 when click the button */} 
      <p>count is: {count}</p>
      <button onClick={handleClickButton}>增加</button>
    </div>
  );
}

Link to code example: https://codesandbox.io/s/batch-update-in-native-xpwz33-xpwz33?file=/src/App.js:0-581

The current behavior

When a setState is executed first in the synchronous execution context, all setState in the microtask queue will not be batched, even if the synchronous execution context still has setState after these microtasks.

The expected behavior

All updates added to the microtask queue should be batched when the code executes to the last update on the synchronous execution stack.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Resolution: StaleAutomatically closed due to inactivityStatus: UnconfirmedA potential issue that we haven't yet confirmed as a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions