Description
React version: 18.2.0
Steps To Reproduce
We learn that components should not rely on render. But useTransition does seem to rely on it. If we wrap a set into a useTransition:
const [pending, start] = useTransition()
...
start(() => set("foo"))
everything that now suspends because of it is marked as a transition, it will not go into suspense fallback but instead pending
will be true. ✅
But if the suspending component, for whatever reason, happens to render again while still in suspense, giving react the same promise that it already has, then it will unpend because that render wasn't marked a transition. ❌
This at least happens when state managers are built on uSES.
Link to code example:
https://codesandbox.io/s/cool-fast-pcz6bv?file=/src/App.js
The current behavior
1. ❌ Fallback, loading...
2. ⚛️ Content
3. ❌ Fallback, loading...
4. ⚛️ Content
In that sandbox it:
- suspends, it correctly goes into suspense fallback
- unsuspends, it correctly shows the content
- suspends again but wrapped into startTransition, it should not go into fallback but pend, but because the suspending component double renders for whatever reason it throws it's promise again towards react, but nothing marked it a transition this time, so the pending state disappears and it goes back into fallback
The expected behavior
1. ❌ Fallback, loading...
2. ⚛️ Content
3. ⚛️ Content
✅ Pending ...
4. ⚛️ Content
startTransition should mark the component that suspended a transition as long as it suspends.
btw, @dai-shi has an extension that lets us use zustand without uSES, it does not happen then: https://codesandbox.io/s/suspicious-hooks-hdvxhl?file=/src/App.js