mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
VirtualizedList: Delete Batchinator (#48515)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/48515 Deletes `Batchinator`, inlines the timer, and cleans up the `disableInteractionManagerInBatchinator` feature flag. Changelog: [Internal] Reviewed By: javache, NickGerleman Differential Revision: D67885194 fbshipit-source-id: 5f3ec71a02cf1f1b382b41a480beed28fc8c5439
This commit is contained in:
committed by
Facebook GitHub Bot
parent
ff0bcb2427
commit
c8a387c2d1
@@ -9856,7 +9856,6 @@ exports[`public API should not change unintentionally src/private/featureflags/R
|
||||
animatedShouldDebounceQueueFlush: Getter<boolean>,
|
||||
animatedShouldUseSingleOp: Getter<boolean>,
|
||||
disableInteractionManager: Getter<boolean>,
|
||||
disableInteractionManagerInBatchinator: Getter<boolean>,
|
||||
enableAccessToHostTreeInFabric: Getter<boolean>,
|
||||
enableAnimatedAllowlist: Getter<boolean>,
|
||||
enableAnimatedClearImmediateFix: Getter<boolean>,
|
||||
@@ -9927,7 +9926,6 @@ declare export const jsOnlyTestFlag: Getter<boolean>;
|
||||
declare export const animatedShouldDebounceQueueFlush: Getter<boolean>;
|
||||
declare export const animatedShouldUseSingleOp: Getter<boolean>;
|
||||
declare export const disableInteractionManager: Getter<boolean>;
|
||||
declare export const disableInteractionManagerInBatchinator: Getter<boolean>;
|
||||
declare export const enableAccessToHostTreeInFabric: Getter<boolean>;
|
||||
declare export const enableAnimatedAllowlist: Getter<boolean>;
|
||||
declare export const enableAnimatedClearImmediateFix: Getter<boolean>;
|
||||
|
||||
@@ -529,16 +529,6 @@ const definitions: FeatureFlagDefinitions = {
|
||||
purpose: 'experimentation',
|
||||
},
|
||||
},
|
||||
disableInteractionManagerInBatchinator: {
|
||||
defaultValue: false,
|
||||
metadata: {
|
||||
dateAdded: '2024-11-18',
|
||||
description:
|
||||
'Skips InteractionManager in `Batchinator` and invokes callbacks synchronously.',
|
||||
expectedReleaseValue: true,
|
||||
purpose: 'experimentation',
|
||||
},
|
||||
},
|
||||
enableAccessToHostTreeInFabric: {
|
||||
defaultValue: false,
|
||||
metadata: {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @generated SignedSource<<8ab1581a5660c8148c1ff87e12c042cc>>
|
||||
* @generated SignedSource<<e710f456c7fa57f98e46cbc467755503>>
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
@@ -31,7 +31,6 @@ export type ReactNativeFeatureFlagsJsOnly = $ReadOnly<{
|
||||
animatedShouldDebounceQueueFlush: Getter<boolean>,
|
||||
animatedShouldUseSingleOp: Getter<boolean>,
|
||||
disableInteractionManager: Getter<boolean>,
|
||||
disableInteractionManagerInBatchinator: Getter<boolean>,
|
||||
enableAccessToHostTreeInFabric: Getter<boolean>,
|
||||
enableAnimatedAllowlist: Getter<boolean>,
|
||||
enableAnimatedClearImmediateFix: Getter<boolean>,
|
||||
@@ -120,11 +119,6 @@ export const animatedShouldUseSingleOp: Getter<boolean> = createJavaScriptFlagGe
|
||||
*/
|
||||
export const disableInteractionManager: Getter<boolean> = createJavaScriptFlagGetter('disableInteractionManager', false);
|
||||
|
||||
/**
|
||||
* Skips InteractionManager in `Batchinator` and invokes callbacks synchronously.
|
||||
*/
|
||||
export const disableInteractionManagerInBatchinator: Getter<boolean> = createJavaScriptFlagGetter('disableInteractionManagerInBatchinator', false);
|
||||
|
||||
/**
|
||||
* Enables access to the host tree in Fabric using DOM-compatible APIs.
|
||||
*/
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {InteractionManager} from 'react-native';
|
||||
import * as ReactNativeFeatureFlags from 'react-native/src/private/featureflags/ReactNativeFeatureFlags';
|
||||
|
||||
/**
|
||||
* A simple class for batching up invocations of a low-pri callback. A timeout is set to run the
|
||||
* callback once after a delay, no matter how many times it's scheduled. Once the delay is reached,
|
||||
* InteractionManager.runAfterInteractions is used to invoke the callback after any hi-pri
|
||||
* interactions are done running.
|
||||
*
|
||||
* Make sure to cleanup with dispose(). Example:
|
||||
*
|
||||
* class Widget extends React.Component {
|
||||
* _batchedSave: new Batchinator(() => this._saveState, 1000);
|
||||
* _saveSate() {
|
||||
* // save this.state to disk
|
||||
* }
|
||||
* componentDidUpdate() {
|
||||
* this._batchedSave.schedule();
|
||||
* }
|
||||
* componentWillUnmount() {
|
||||
* this._batchedSave.dispose();
|
||||
* }
|
||||
* ...
|
||||
* }
|
||||
*/
|
||||
class Batchinator {
|
||||
_callback: () => void;
|
||||
_delay: number;
|
||||
_taskHandle: ?{cancel: () => void, ...};
|
||||
|
||||
constructor(callback: () => void, delay: number) {
|
||||
this._delay = delay;
|
||||
this._callback = callback;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cleanup any pending tasks.
|
||||
*
|
||||
* By default, if there is a pending task the callback is run immediately. Set the option abort to
|
||||
* true to not call the callback if it was pending.
|
||||
*/
|
||||
dispose(): void {
|
||||
if (this._taskHandle) {
|
||||
this._taskHandle.cancel();
|
||||
this._taskHandle = null;
|
||||
}
|
||||
}
|
||||
|
||||
schedule(): void {
|
||||
if (this._taskHandle) {
|
||||
return;
|
||||
}
|
||||
const invokeCallback = () => {
|
||||
// Note that we clear the handle before invoking the callback so that if the callback calls
|
||||
// schedule again, it will actually schedule another task.
|
||||
this._taskHandle = null;
|
||||
this._callback();
|
||||
};
|
||||
|
||||
const timeoutHandle = setTimeout(
|
||||
// NOTE: When shipping this, delete `Batchinator` instead of only these
|
||||
// lines of code. Without `InteractionManager`, it's just a `setTimeout`.
|
||||
ReactNativeFeatureFlags.disableInteractionManagerInBatchinator()
|
||||
? invokeCallback
|
||||
: () => {
|
||||
this._taskHandle =
|
||||
InteractionManager.runAfterInteractions(invokeCallback);
|
||||
},
|
||||
this._delay,
|
||||
);
|
||||
this._taskHandle = {cancel: () => clearTimeout(timeoutHandle)};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Batchinator;
|
||||
@@ -1,74 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
describe('Batchinator', () => {
|
||||
const Batchinator = require('../Batchinator');
|
||||
|
||||
it('executes vanilla tasks', () => {
|
||||
const callback = jest.fn();
|
||||
const batcher = new Batchinator(callback, 10000);
|
||||
batcher.schedule();
|
||||
jest.runAllTimers();
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('batches up tasks', () => {
|
||||
const callback = jest.fn();
|
||||
const batcher = new Batchinator(callback, 10000);
|
||||
batcher.schedule();
|
||||
batcher.schedule();
|
||||
batcher.schedule();
|
||||
batcher.schedule();
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
jest.runAllTimers();
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('does nothing after dispose', () => {
|
||||
const callback = jest.fn();
|
||||
const batcher = new Batchinator(callback, 10000);
|
||||
batcher.schedule();
|
||||
batcher.schedule();
|
||||
batcher.dispose();
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
jest.runAllTimers();
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call tasks scheduled by the callback', () => {
|
||||
let batcher = null;
|
||||
let hasRescheduled = false;
|
||||
const callback = jest.fn(() => {
|
||||
if (!hasRescheduled) {
|
||||
batcher.schedule();
|
||||
hasRescheduled = true;
|
||||
}
|
||||
});
|
||||
batcher = new Batchinator(callback, 10000);
|
||||
batcher.schedule();
|
||||
jest.runAllTimers();
|
||||
expect(callback.mock.calls.length).toBe(2);
|
||||
});
|
||||
|
||||
it('does not run callbacks more than once', () => {
|
||||
const callback = jest.fn();
|
||||
const batcher = new Batchinator(callback, 10000);
|
||||
batcher.schedule();
|
||||
batcher.schedule();
|
||||
jest.runAllTimers();
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
jest.runAllTimers();
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
batcher.dispose();
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -24,7 +24,6 @@ import type {
|
||||
ScrollEvent,
|
||||
} from 'react-native/Libraries/Types/CoreEventTypes';
|
||||
|
||||
import Batchinator from '../Interaction/Batchinator';
|
||||
import clamp from '../Utilities/clamp';
|
||||
import infoLog from '../Utilities/infoLog';
|
||||
import {CellRenderMask} from './CellRenderMask';
|
||||
@@ -375,10 +374,6 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
||||
this._checkProps(props);
|
||||
|
||||
this._fillRateHelper = new FillRateHelper(this._listMetrics);
|
||||
this._updateCellsToRenderBatcher = new Batchinator(
|
||||
this._updateCellsToRender,
|
||||
this.props.updateCellsBatchingPeriod ?? 50,
|
||||
);
|
||||
|
||||
if (this.props.viewabilityConfigCallbackPairs) {
|
||||
this._viewabilityTuples = this.props.viewabilityConfigCallbackPairs.map(
|
||||
@@ -687,7 +682,7 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
||||
if (this._isNestedWithSameOrientation()) {
|
||||
this.context.unregisterAsNestedChild({ref: this});
|
||||
}
|
||||
this._updateCellsToRenderBatcher.dispose();
|
||||
clearTimeout(this._updateCellsToRenderTimeoutID);
|
||||
this._viewabilityTuples.forEach(tuple => {
|
||||
tuple.viewabilityHelper.dispose();
|
||||
});
|
||||
@@ -1228,7 +1223,7 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
||||
_scrollRef: ?React.ElementRef<any> = null;
|
||||
_sentStartForContentLength = 0;
|
||||
_sentEndForContentLength = 0;
|
||||
_updateCellsToRenderBatcher: Batchinator;
|
||||
_updateCellsToRenderTimeoutID: ?TimeoutID = null;
|
||||
_viewabilityTuples: Array<ViewabilityHelperCallbackTuple> = [];
|
||||
|
||||
/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's
|
||||
@@ -1763,11 +1758,19 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
||||
this._hiPriInProgress = true;
|
||||
// Don't worry about interactions when scrolling quickly; focus on filling content as fast
|
||||
// as possible.
|
||||
this._updateCellsToRenderBatcher.dispose();
|
||||
if (this._updateCellsToRenderTimeoutID != null) {
|
||||
clearTimeout(this._updateCellsToRenderTimeoutID);
|
||||
this._updateCellsToRenderTimeoutID = null;
|
||||
}
|
||||
this._updateCellsToRender();
|
||||
return;
|
||||
} else {
|
||||
this._updateCellsToRenderBatcher.schedule();
|
||||
if (this._updateCellsToRenderTimeoutID == null) {
|
||||
this._updateCellsToRenderTimeoutID = setTimeout(() => {
|
||||
this._updateCellsToRenderTimeoutID = null;
|
||||
this._updateCellsToRender();
|
||||
}, this.props.updateCellsBatchingPeriod ?? 50);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user