mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
Stacked on #32783. This will replace [the `useSwipeTransition` API](https://github.com/facebook/react/pull/32373). Instead, of a special Hook, you can make updates to `useOptimistic` Hooks within the `startGestureTransition` scope. ``` import {unstable_startGestureTransition as startGestureTransition} from 'react'; const cancel = startGestureTransition(timeline, () => { setOptimistic(...); }, options); ``` There are some downsides to this like you can't define two directions as once and there's no "standard" direction protocol. It's instead up to libraries to come up with their own conventions (although we can suggest some). The convention is still that a gesture recognizer has two props `action` and `gesture`. The `gesture` prop is a Gesture concept which now behaves more like an Action but 1) it can't be async 2) it shouldn't have side-effects. For example you can't call `setState()` in it except on `useOptimistic` since those can be reverted if needed. The `action` is invoked with whatever side-effects you want after the gesture fulfills. This is isomorphic and not associated with a specific renderer nor root so it's a bit more complicated. To implement this I unify with the `ReactSharedInternal.T` property to contain a regular Transition or a Gesture Transition (the `gesture` field). The benefit of this unification means that every time we override this based on some scope like entering `flushSync` we also override the `startGestureTransition` scope. We just have to be careful when we read it to check the `gesture` field to know which one it is. (E.g. I error for setState / requestFormReset.) The other thing that's unique is the `cancel` return value to know when to stop the gesture. That cancellation is no longer associated with any particular Hook. It's more associated with the scope of the `startGestureTransition`. Since the schedule of whether a particular gesture has rendered or committed is associated with a root, we need to somehow associate any scheduled gestures with a root. We could track which roots we update inside the scope but instead, I went with a model where I check all the roots and see if there's a scheduled gesture matching the timeline. This means that you could "retain" a gesture across roots. Meaning this wouldn't cancel until both are cancelled: ``` const cancelA = startGestureTransition(timeline, () => { setOptimisticOnRootA(...); }, options); const cancelB = startGestureTransition(timeline, () => { setOptimisticOnRootB(...); }, options); ``` It's more like it's a global transition than associated with the roots that were updated. Optimistic updates mostly just work but I now associate them with a specific "ScheduledGesture" instance since we can only render one at a time and so if it's not the current one, we leave it for later. Clean up of optimistic updates is now lazy rather than when we cancel. Allowing the cancel closure not to have to be associated with each particular update.
60 KiB
60 KiB