Skip to content

Commit df98233

Browse files
authored
Replace setImmediate and requestAnimationFrame with queueMicrotask (#2467)
## Description - `setImmediate` is not available on web & when using server-side-rendering - `requestAnimationFrame` is not available when using server-side-rendering ## Test plan Test on Example and FabricExample apps.
1 parent 651ee82 commit df98233

File tree

4 files changed

+23
-25
lines changed

4 files changed

+23
-25
lines changed

src/handlers/createHandler.tsx

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -174,16 +174,17 @@ export default function createHandler<
174174
private handlerTag: number;
175175
private config: Record<string, unknown>;
176176
private propsRef: React.MutableRefObject<unknown>;
177+
private isMountedRef: React.MutableRefObject<boolean | null>;
177178
private viewNode: any;
178179
private viewTag?: number;
179-
private updateEnqueued: ReturnType<typeof setImmediate> | null = null;
180180
private inspectorToggleListener?: EmitterSubscription;
181181

182182
constructor(props: T & InternalEventHandlers) {
183183
super(props);
184184
this.handlerTag = getNextHandlerTag();
185185
this.config = {};
186186
this.propsRef = React.createRef();
187+
this.isMountedRef = React.createRef();
187188
this.state = { allowTouches };
188189
if (props.id) {
189190
if (handlerIDToTag[props.id] !== undefined) {
@@ -195,6 +196,7 @@ export default function createHandler<
195196

196197
componentDidMount() {
197198
const props: HandlerProps<U> = this.props;
199+
this.isMountedRef.current = true;
198200

199201
if (DEV_ON_ANDROID) {
200202
this.inspectorToggleListener = DeviceEventEmitter.addListener(
@@ -209,11 +211,10 @@ export default function createHandler<
209211
// If there are unresolved refs (e.g. ".current" has not yet been set)
210212
// passed as `simultaneousHandlers` or `waitFor`, we enqueue a call to
211213
// _update method that will try to update native handler props using
212-
// setImmediate. This makes it so update() function gets called after all
214+
// queueMicrotask. This makes it so update() function gets called after all
213215
// react components are mounted and we expect the missing ref object to
214216
// be resolved by then.
215-
this.updateEnqueued = setImmediate(() => {
216-
this.updateEnqueued = null;
217+
queueMicrotask(() => {
217218
this.update(UNRESOLVED_REFS_RETRY_LIMIT);
218219
});
219220
}
@@ -239,11 +240,9 @@ export default function createHandler<
239240

240241
componentWillUnmount() {
241242
this.inspectorToggleListener?.remove();
243+
this.isMountedRef.current = false;
242244
RNGestureHandlerModule.dropGestureHandler(this.handlerTag);
243245
scheduleFlushOperations();
244-
if (this.updateEnqueued) {
245-
clearImmediate(this.updateEnqueued);
246-
}
247246
// We can't use this.props.id directly due to TS generic type narrowing bug, see https://github.com/microsoft/TypeScript/issues/13995 for more context
248247
const handlerID: string | undefined = this.props.id;
249248
if (handlerID) {
@@ -367,14 +366,17 @@ export default function createHandler<
367366
};
368367

369368
private update(remainingTries: number) {
369+
if (!this.isMountedRef.current) {
370+
return;
371+
}
372+
370373
const props: HandlerProps<U> = this.props;
371374

372375
// When ref is set via a function i.e. `ref={(r) => refObject.current = r}` instead of
373376
// `ref={refObject}` it's possible that it won't be resolved in time. Seems like trying
374377
// again is easy enough fix.
375378
if (hasUnresolvedRefs(props) && remainingTries > 0) {
376-
this.updateEnqueued = setImmediate(() => {
377-
this.updateEnqueued = null;
379+
queueMicrotask(() => {
378380
this.update(remainingTries - 1);
379381
});
380382
} else {

src/handlers/gestureHandlerCommon.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,16 +191,15 @@ export function findNodeHandle(
191191
return findNodeHandleRN(node);
192192
}
193193

194-
let scheduledFlushOperationsId: ReturnType<
195-
typeof requestAnimationFrame
196-
> | null = null;
194+
let flushOperationsScheduled = false;
197195

198196
export function scheduleFlushOperations() {
199-
if (scheduledFlushOperationsId === null) {
200-
scheduledFlushOperationsId = requestAnimationFrame(() => {
197+
if (!flushOperationsScheduled) {
198+
flushOperationsScheduled = true;
199+
queueMicrotask(() => {
201200
RNGestureHandlerModule.flushOperations();
202201

203-
scheduledFlushOperationsId = null;
202+
flushOperationsScheduled = false;
204203
});
205204
}
206205
}

src/handlers/gestures/GestureDetector.tsx

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,6 @@ export type GestureConfigReference = {
7171
useReanimatedHook: boolean;
7272
};
7373

74-
const scheduleUpdate =
75-
Platform.OS === 'web' ? requestAnimationFrame : setImmediate;
76-
7774
function convertToHandlerTag(ref: GestureRef): number {
7875
if (typeof ref === 'number') {
7976
return ref;
@@ -153,9 +150,9 @@ function attachHandlers({
153150
preparedGesture.firstExecution = false;
154151
}
155152

156-
// use scheduleUpdate to extract handlerTags, because all refs should be initialized
153+
// use queueMicrotask to extract handlerTags, because all refs should be initialized
157154
// when it's ran
158-
scheduleUpdate(() => {
155+
queueMicrotask(() => {
159156
if (!mountedRef.current) {
160157
return;
161158
}
@@ -173,9 +170,9 @@ function attachHandlers({
173170
registerHandler(handler.handlerTag, handler, handler.config.testId);
174171
}
175172

176-
// use scheduleUpdate to extract handlerTags, because all refs should be initialized
173+
// use queueMicrotask to extract handlerTags, because all refs should be initialized
177174
// when it's ran
178-
scheduleUpdate(() => {
175+
queueMicrotask(() => {
179176
if (!mountedRef.current) {
180177
return;
181178
}
@@ -260,10 +257,10 @@ function updateHandlers(
260257
}
261258
}
262259

263-
// use scheduleUpdate to extract handlerTags, because when it's ran, all refs should be updated
260+
// use queueMicrotask to extract handlerTags, because when it's ran, all refs should be updated
264261
// and handlerTags in BaseGesture references should be updated in the loop above (we need to wait
265262
// in case of external relations)
266-
scheduleUpdate(() => {
263+
queueMicrotask(() => {
267264
if (!mountedRef.current) {
268265
return;
269266
}

src/web_hammer/GestureHandler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ abstract class GestureHandler {
508508
.filter((v) => v);
509509

510510
if (shouldUseTouchEvents !== this.shouldUseTouchEvents(props)) {
511-
requestAnimationFrame(() => {
511+
queueMicrotask(() => {
512512
// if the undelying event API needs to be changed, we need to unmount and mount
513513
// the hammer instance again.
514514
this.destroy();

0 commit comments

Comments
 (0)