I wrote a simple animation hook that needs to wait for React to commit renders (for concurrent mode) before painting updates. I have something like this:
function useAnimation() {
const hasCommittedRef = useRef(false);
hasCommittedRef.current = false;
useLayoutEffect(() => {
hasCommittedRef.current = true;
});
const handleUpdate = () => {
if (!hasCommittedRef.current) {
// don't paint yet
}
// paint
};
}
Without Suspense, this works perfectly. With Suspense, if the component that calls this hook renders, suspends, then unsuspends: hasCommittedRef.current
would always stay false
.
E.g. here’s a Codesandbox demo: https://codesandbox.io/p/sandbox/youthful-fast-7vzmkz?file=%2Fsrc%2FApp.js%3A4%2C1
import React from "react";
const infiniteThenable = { then() {} };
function Inner() {
console.log("Inner");
const [isSuspended, setIsSuspended] = React.useState(true);
React.useEffect(() => {
setTimeout(() => {
setIsSuspended(false);
}, 500);
}, []);
if (isSuspended) {
throw infiniteThenable;
}
}
export default function App() {
console.log("App");
React.useLayoutEffect(() => {
console.log("App effect");
});
return (
<React.Suspense>
<Inner />
</React.Suspense>
);
}
This logs:
App
Inner
App effect
Inner
Since the useLayoutEffect
doesn’t run again, I can’t use this method to check if there’s a pending commit. Is there a way to check for pending commits that works with Suspense?