Bug: It seems that the concurrent mode does not work as expect

This issue has been created since 2022-09-14.

React version: 17.0.1

Steps To Reproduce

Here I use a while loop to simulate a long task.When I click on the div, the animation stops

import React from "react";
import ReactDOM from "react-dom";

const NumberComp = ({ count }) => {
  const start = new Date().getTime();
// Simulate time-consuming tasks
  while (new Date().getTime() - start < 1) {}
  return count;
};
const arr = [];
for (let i = 0; i < 1000; i++) {
  arr.push(i);
}
class Home extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
  }

  render() {
    return (
      <>
        <div
          onClick={() => this.setState({ count: this.state.count + 1 })}
          className="animation"
        >{`count:${this.state.count}`}</div>
        {arr.map((i) => (
          <NumberComp key={i} count={i} />
        ))}
      </>
    );
  }
}

ReactDOM.unstable_createRoot(document.getElementById("root")).render(<Home />);

the css

.animation {
    display: block;
     width: 100px;
     height: 100px;
     background: lightyellow;
     animation: myfirst 5s;
     animation-iteration-count: infinite;
}
@keyframes myfirst {
   from {
        width: 30px;
        height: 30px;
        border-radius: 0;
  }
      to {
        width: 200px;
        height: 200px;
        border-radius: 50%;
      }
}

The current behavior

When I click on the div, the animation stops.And the performance is as follows:

image

It seems that the concurrent mode does not work

The expected behavior

Since it's concurrent mode, when I click on the div, the animation shouldn't stop and the page shouldn't freeze

gaearon wrote this answer on 2022-09-15

Not sure about how this works in experimental 17 builds, but the new behavior in 18 is opt-in.

I slightly changed your code to sum up the two counters so that it's clearer that the prop gets passed down.

Here's a version that wraps the update in startTransition. So the counter doesn't update until everything is done:

https://codesandbox.io/s/inspiring-danilo-u9t0kr?file=/src/App.js

import { useState, startTransition } from "react";

const NumberComp = ({ count, parentCount }) => {
  const start = performance.now();
  // Simulate time-consuming tasks
  while (performance.now() - start < 1) {}
  return count + parentCount + " ";
};
const arr = [];
for (let i = 0; i < 1000; i++) {
  arr.push(i);
}

const Home = () => {
  const [count, setCount] = useState(0);

  function handleClick() {
    startTransition(() => {
      setCount((c) => c + 1);
    });
  }

  return (
    <>
      <div onClick={handleClick} className="animation">{`count:${count}`}</div>
      {arr.map((i) => (
        <NumberComp key={i} count={i} parentCount={count} />
      ))}
    </>
  );
};

Here's a version with visual indication that something is happening via changing opacity and useTransition:

https://codesandbox.io/s/damp-leftpad-6m6s6p?file=/src/App.js

import { memo, useState, useTransition } from "react";

const NumberComp = ({ count, parentCount }) => {
  const start = performance.now();
  // Simulate time-consuming tasks
  while (performance.now() - start < 1) {}
  return count + parentCount + " ";
};

const arr = [];
for (let i = 0; i < 1000; i++) {
  arr.push(i);
}

const ExpensiveTree = memo(({ parentCount }) => {
  return arr.map((i) => (
    <NumberComp key={i} count={i} parentCount={parentCount} />
  ));
});

const Home = () => {
  const [count, setCount] = useState(0);
  const [isPending, startTransition] = useTransition();

  function handleClick() {
    startTransition(() => {
      setCount((c) => c + 1);
    });
  }

  return (
    <>
      <div
        onClick={handleClick}
        className="animation"
        style={{
          opacity: isPending ? 0.5 : 1
        }}
      >{`count:${count}`}</div>
      <ExpensiveTree parentCount={count} />
    </>
  );
};

Here's a version with useDeferredValue that lets the parent update immediately but the children "lag behind":

https://codesandbox.io/s/brave-thompson-du3y1d?file=/src/App.js

const NumberComp = ({ count, parentCount }) => {
  const start = performance.now();
  // Simulate time-consuming tasks
  while (performance.now() - start < 1) {}
  return count + parentCount + " ";
};

const arr = [];
for (let i = 0; i < 1000; i++) {
  arr.push(i);
}

const ExpensiveTree = memo(({ parentCount }) => {
  return arr.map((i) => (
    <NumberComp key={i} count={i} parentCount={parentCount} />
  ));
});

const Home = () => {
  const [count, setCount] = useState(0);
  const deferredCount = useDeferredValue(count);

  function handleClick() {
    setCount((c) => c + 1);
  }

  return (
    <>
      <div
        onClick={handleClick}
        className="animation"
        style={{
          opacity: count === deferredCount ? 1 : 0.5
        }}
      >
        <b>{`count:${count}`}</b>
      </div>
      <ExpensiveTree parentCount={deferredCount} />
    </>
  );
};

Hope this helps!

lizuncong wrote this answer on 2022-09-15

My demo can also be reproduced in react 18. So I can't call setState directly in react 18, but should I wrap it with useTransition

lizuncong wrote this answer on 2022-09-15

Oh I see, this is not a bug. When I click on the div, the scheduler will schedule a concurrent rendering task with a priority of UserBlockingPriority, and the corresponding expiration time is 250ms. In my demo, since 1000 Number components were rendered, each component took 1ms, and in the previous 250 components it took 250ms. So starting from the 251st component, since the current rendering task has expired, react schedules a synchronous rendering task with the priority ImmediatePriority, so a long task of 750 milliseconds is created, causing the page to freeze

gaearon wrote this answer on 2022-09-15

My demo can also be reproduced in react 18. So I can't call setState directly in react 18, but should I wrap it with useTransition

Updating state during events like clicks is synchronous because the user expects feedback immediately. So yes, deferred updates and concurrency is opt-in.

More Details About Repo
Owner Name facebook
Repo Name react
Full Name facebook/react
Language JavaScript
Created Date 2013-05-24
Updated Date 2022-09-27
Star Count 195296
Watcher Count 6655
Fork Count 40431
Issue Count 1113

YOU MAY BE INTERESTED

Issue Title Created Date Comment Count Updated Date
Re-tag 1.5.0 as v1.5.0 instead of v1.5 2 2021-11-09 2022-07-27
trivy scanner reports CVE in docker hub image 3 2021-05-26 2022-08-04
UnboundLocalError: local variable 'dotAB' referenced before assignment 5 2021-10-13 2022-05-28
UnboundLocalError: local variable 'dotAB' referenced before assignment in dot23() 2 2021-04-29 2022-05-28
v1 and v6 are not entirely threadsafe 4 2020-08-12 2022-09-16
Parse UUID string with URN prefix and curly braces 0 2020-09-07 2022-09-16
Change the DefaultTimestampStrategy to calculate the elapsed time between calls 3 2020-11-29 2022-09-16
completing citation information for illustris 0 2021-04-05 2022-09-26
希望增加算法目录,同时改进算法的计数方式 3 2022-06-29 2022-09-16
Setting attachment filename doesn't do anything 3 2021-07-07 2022-08-09
[Feature] - Marketplace support 0 2021-07-12 2022-09-26
Misconfigured redis server 0 2022-01-28 2022-09-05
Error when translating docx-document 2 2022-06-14 2022-09-16
fixture.detectChanges() does not trigger ngOnChanges with updates made to fixture.point.componentInstance 3 2022-05-01 2022-09-18
configure.in should be renamed to configure.ac, AC_INIT() and AM_INIT_AUTOMAKE() calls 1 2021-02-11 2022-09-26
NvChad not loading plugins automatically on startup 7 2022-09-23 2022-09-27
Entscheidungswert kein Lösch Button 4 2021-10-07 2022-09-05
Can anyone add pspell extension ? 1 2022-03-29 2022-09-16
Running ConditionalEvaluation requires manual Whitebox library folder permissions change 2 2022-02-21 2022-09-25
Firefox cursors are using default X server cursors and don't always change with context on websites 2 2021-10-29 2022-09-26
BVM Sync Calls & Multiple Shaders 0 2021-12-06 2022-09-15
Update do higer version is inaccessible 2 2021-06-22 2022-07-17
[changelog]: remove the border below the last item on changelog entry. 0 2021-11-23 2022-09-25
[Enhancement]: Reduce memory usage when load index 2 2022-03-24 2022-08-16
View License shows "GPL" instead of LGPL 2 2021-06-28 2022-08-14
Add `partial` and `return` template functions 7 2022-08-22 2022-09-15
Can't update system firmware with fwupd 2 2020-10-01 2022-09-01
Revise documentation hovering 2 2020-10-23 2022-07-28
Access Denied in device.open : Webusb 10 2020-03-16 2022-09-23
/bin/sh: 1: /var/scripts/spamhaus_crontab.sh: not found 1 2021-08-14 2022-08-30
Пожелания по новому Помощнику 1 2020-06-30 2022-09-24
Can't change serial port line ending sequence 1 2021-08-21 2022-09-11
SQLSTATE[HY000] [2002] No such file or directory (SQL: select * from inform 7 2021-05-28 2022-09-22
Problems starting a 1.12.2 modded server 1 2021-03-04 2022-09-13
How to Read a DataFrameDirectory in a custom component 0 2022-05-04 2022-09-25
Multi arch builds fail on DroneCi in Docker 1 2021-08-14 2022-09-23
Tag Versions 1 2021-12-01 2022-01-15
Debug core crashes RA on load content 2 2021-08-10 2022-09-17
[bug] Can't build project 2 2022-03-12 2022-09-15
Bump path-parse from 1.0.6 to 1.0.7 0 2021-08-12 2022-02-08
LuCI版本里面,缺少打开硬件NAT(HWNAT)的菜单项 4 2021-12-14 2022-09-14
如果可以,真心希望少改动默认的勾选项。 5 2021-12-14 2021-12-20
Allow update of deCONZ to install pre-release versions 1 2021-12-03 2022-09-24
Support Frictionless Data package as a source of metadata 0 2021-04-12 2022-09-18
Display error 6 2021-09-19 2022-01-16
Synchronous StreamControllers in bloc pattern? 1 2019-12-13 2022-09-22
UI - As User - Register/Upload a Template/ISO - Form should not have the Featured field available 0 2021-10-20 2022-08-15
[Security Solution] Incorrect validation message is displaying for the `Fields` 5 2022-07-26 2022-09-25
Fix broken headings in Markdown files 0 2017-04-17 2022-09-16
Wrong syntax highlighting when using exponents with scientific notation, like 6.626e-34 0 2021-04-29 2022-09-24