Description
React version: 19.0.0
Related Issues: #30408, #31697
This may be the intended behavior, but I'm opening this issue anyway because enough discussion hasn't been done in the related issues IMO and the current behavior still feels poor to me in that it makes it too easy to slow down actual user experience.
Steps To Reproduce
Code
const zero = Promise.resolve(0);
function getNumber(num) {
return new Promise((resolve) => {
setTimeout(() => resolve(num), 100);
});
}
function App() {
const [count, setCount] = useState(zero);
const countValue = use(count);
useEffect(() => {
console.log("countValue =", countValue, Date.now());
}, [countValue]);
return (
<button
onClick={() => {
console.log("click", Date.now());
setCount(getNumber(countValue + 1));
}}
>
count is {countValue}
</button>
);
}
In short, when a rerendering suspends, you always have to wait for 300ms even if underlying data fetching has finished sooner.
In the attached example, when user pushes the button, a new Promise is passed to use()
, which triggers Suspense. Even though that Promise resolves exactly after 100ms, the UI is updated only after 300ms.
I experienced this issue when using Jotai, a Suspense-based state management library.
Given that the throttling behavior kicks in even in this simplest situation, it seems impossible to implement a user experience that involves Suspension and is quicker than 300ms regardless of, say, user's network speed.
Link to code example:
https://codesandbox.io/p/sandbox/4f4r94
The current behavior
Almost always need to wait for 300ms.
The expected behavior
Maybe some better heuristic for enabling the throttling behavior? It would also be very nice to make this configurable.