mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Animated: Feature Flags Cleanup (#48509)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/48509 Cleans up the following feature flags from `Animated`: - `enableAnimatedAllowlist` - `enableAnimatedPropsMemo` - `useInsertionEffectsForAnimations` This will significantly simplify some future planned work here (e.g. T209740497). Changelog: [Internal] Reviewed By: javache Differential Revision: D67867115 fbshipit-source-id: adf35c70d95f42c240342fda3b4f2e9b4bdfe30a
This commit is contained in:
committed by
Facebook GitHub Bot
parent
0883207e44
commit
ab77bdf471
@@ -24,8 +24,6 @@ import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useInsertionEffect,
|
||||
useLayoutEffect,
|
||||
useMemo,
|
||||
useReducer,
|
||||
useRef,
|
||||
} from 'react';
|
||||
@@ -44,11 +42,6 @@ type AnimatedValueListeners = Array<{
|
||||
listenerId: string,
|
||||
}>;
|
||||
|
||||
const useMemoOrAnimatedPropsMemo =
|
||||
ReactNativeFeatureFlags.enableAnimatedPropsMemo()
|
||||
? useAnimatedPropsMemo
|
||||
: useMemo;
|
||||
|
||||
export default function useAnimatedProps<TProps: {...}, TInstance>(
|
||||
props: TProps,
|
||||
allowlist?: ?AnimatedPropsAllowlist,
|
||||
@@ -57,28 +50,14 @@ export default function useAnimatedProps<TProps: {...}, TInstance>(
|
||||
const onUpdateRef = useRef<UpdateCallback | null>(null);
|
||||
const timerRef = useRef<TimeoutID | null>(null);
|
||||
|
||||
const allowlistIfEnabled = ReactNativeFeatureFlags.enableAnimatedAllowlist()
|
||||
? allowlist
|
||||
: null;
|
||||
|
||||
const node = useMemoOrAnimatedPropsMemo(
|
||||
() =>
|
||||
new AnimatedProps(
|
||||
props,
|
||||
() => onUpdateRef.current?.(),
|
||||
allowlistIfEnabled,
|
||||
),
|
||||
[allowlistIfEnabled, props],
|
||||
const node = useAnimatedPropsMemo(
|
||||
() => new AnimatedProps(props, () => onUpdateRef.current?.(), allowlist),
|
||||
[allowlist, props],
|
||||
);
|
||||
|
||||
const useNativePropsInFabric =
|
||||
ReactNativeFeatureFlags.shouldUseSetNativePropsInFabric();
|
||||
|
||||
const useAnimatedPropsLifecycle =
|
||||
ReactNativeFeatureFlags.useInsertionEffectsForAnimations()
|
||||
? useAnimatedPropsLifecycle_insertionEffects
|
||||
: useAnimatedPropsLifecycle_layoutEffects;
|
||||
|
||||
useAnimatedPropsLifecycle(node);
|
||||
|
||||
// TODO: This "effect" does three things:
|
||||
@@ -208,9 +187,7 @@ function reduceAnimatedProps<TProps>(
|
||||
// Force `collapsable` to be false so that the native view is not flattened.
|
||||
// Flattened views cannot be accurately referenced by the native driver.
|
||||
return {
|
||||
...(ReactNativeFeatureFlags.enableAnimatedPropsMemo()
|
||||
? node.__getValueWithStaticProps(props)
|
||||
: node.__getValue()),
|
||||
...node.__getValueWithStaticProps(props),
|
||||
collapsable: false,
|
||||
};
|
||||
}
|
||||
@@ -251,66 +228,7 @@ function addAnimatedValuesListenersToProps(
|
||||
* nodes. So in order to optimize this, we avoid detaching until the next attach
|
||||
* unless we are unmounting.
|
||||
*/
|
||||
function useAnimatedPropsLifecycle_layoutEffects(node: AnimatedProps): void {
|
||||
const prevNodeRef = useRef<?AnimatedProps>(null);
|
||||
const isUnmountingRef = useRef<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
// It is ok for multiple components to call `flushQueue` because it noops
|
||||
// if the queue is empty. When multiple animated components are mounted at
|
||||
// the same time. Only first component flushes the queue and the others will noop.
|
||||
NativeAnimatedHelper.API.flushQueue();
|
||||
let drivenAnimationEndedListener: ?EventSubscription = null;
|
||||
if (node.__isNative) {
|
||||
drivenAnimationEndedListener =
|
||||
NativeAnimatedHelper.nativeEventEmitter.addListener(
|
||||
'onUserDrivenAnimationEnded',
|
||||
data => {
|
||||
node.update();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return () => {
|
||||
drivenAnimationEndedListener?.remove();
|
||||
};
|
||||
});
|
||||
|
||||
useLayoutEffect(() => {
|
||||
isUnmountingRef.current = false;
|
||||
return () => {
|
||||
isUnmountingRef.current = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
node.__attach();
|
||||
if (prevNodeRef.current != null) {
|
||||
const prevNode = prevNodeRef.current;
|
||||
// TODO: Stop restoring default values (unless `reset` is called).
|
||||
prevNode.__restoreDefaultValues();
|
||||
prevNode.__detach();
|
||||
prevNodeRef.current = null;
|
||||
}
|
||||
return () => {
|
||||
if (isUnmountingRef.current) {
|
||||
// NOTE: Do not restore default values on unmount, see D18197735.
|
||||
node.__detach();
|
||||
} else {
|
||||
prevNodeRef.current = node;
|
||||
}
|
||||
};
|
||||
}, [node]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages the lifecycle of the supplied `AnimatedProps` by invoking `__attach`
|
||||
* and `__detach`. However, this is more complicated because `AnimatedProps`
|
||||
* uses reference counting to determine when to recursively detach its children
|
||||
* nodes. So in order to optimize this, we avoid detaching until the next attach
|
||||
* unless we are unmounting.
|
||||
*/
|
||||
function useAnimatedPropsLifecycle_insertionEffects(node: AnimatedProps): void {
|
||||
function useAnimatedPropsLifecycle(node: AnimatedProps): void {
|
||||
const prevNodeRef = useRef<?AnimatedProps>(null);
|
||||
const isUnmountingRef = useRef<boolean>(false);
|
||||
|
||||
|
||||
@@ -9856,16 +9856,13 @@ exports[`public API should not change unintentionally src/private/featureflags/R
|
||||
animatedShouldUseSingleOp: Getter<boolean>,
|
||||
disableInteractionManager: Getter<boolean>,
|
||||
enableAccessToHostTreeInFabric: Getter<boolean>,
|
||||
enableAnimatedAllowlist: Getter<boolean>,
|
||||
enableAnimatedClearImmediateFix: Getter<boolean>,
|
||||
enableAnimatedPropsMemo: Getter<boolean>,
|
||||
fixVirtualizeListCollapseWindowSize: Getter<boolean>,
|
||||
isLayoutAnimationEnabled: Getter<boolean>,
|
||||
shouldSkipStateUpdatesForLoopingAnimations: Getter<boolean>,
|
||||
shouldUseAnimatedObjectForTransform: Getter<boolean>,
|
||||
shouldUseRemoveClippedSubviewsAsDefaultOnIOS: Getter<boolean>,
|
||||
shouldUseSetNativePropsInFabric: Getter<boolean>,
|
||||
useInsertionEffectsForAnimations: Getter<boolean>,
|
||||
useRefsForTextInputState: Getter<boolean>,
|
||||
}>;
|
||||
export type ReactNativeFeatureFlagsJsOnlyOverrides =
|
||||
@@ -9925,16 +9922,13 @@ declare export const animatedShouldDebounceQueueFlush: Getter<boolean>;
|
||||
declare export const animatedShouldUseSingleOp: Getter<boolean>;
|
||||
declare export const disableInteractionManager: Getter<boolean>;
|
||||
declare export const enableAccessToHostTreeInFabric: Getter<boolean>;
|
||||
declare export const enableAnimatedAllowlist: Getter<boolean>;
|
||||
declare export const enableAnimatedClearImmediateFix: Getter<boolean>;
|
||||
declare export const enableAnimatedPropsMemo: Getter<boolean>;
|
||||
declare export const fixVirtualizeListCollapseWindowSize: Getter<boolean>;
|
||||
declare export const isLayoutAnimationEnabled: Getter<boolean>;
|
||||
declare export const shouldSkipStateUpdatesForLoopingAnimations: Getter<boolean>;
|
||||
declare export const shouldUseAnimatedObjectForTransform: Getter<boolean>;
|
||||
declare export const shouldUseRemoveClippedSubviewsAsDefaultOnIOS: Getter<boolean>;
|
||||
declare export const shouldUseSetNativePropsInFabric: Getter<boolean>;
|
||||
declare export const useInsertionEffectsForAnimations: Getter<boolean>;
|
||||
declare export const useRefsForTextInputState: Getter<boolean>;
|
||||
declare export const commonTestFlag: Getter<boolean>;
|
||||
declare export const commonTestFlagWithoutNativeImplementation: Getter<boolean>;
|
||||
|
||||
@@ -529,15 +529,6 @@ const definitions: FeatureFlagDefinitions = {
|
||||
purpose: 'release',
|
||||
},
|
||||
},
|
||||
enableAnimatedAllowlist: {
|
||||
defaultValue: true,
|
||||
metadata: {
|
||||
description:
|
||||
'Enables Animated to skip non-allowlisted props and styles.',
|
||||
expectedReleaseValue: true,
|
||||
purpose: 'release',
|
||||
},
|
||||
},
|
||||
enableAnimatedClearImmediateFix: {
|
||||
defaultValue: true,
|
||||
metadata: {
|
||||
@@ -548,15 +539,6 @@ const definitions: FeatureFlagDefinitions = {
|
||||
purpose: 'experimentation',
|
||||
},
|
||||
},
|
||||
enableAnimatedPropsMemo: {
|
||||
defaultValue: true,
|
||||
metadata: {
|
||||
description:
|
||||
'Enables Animated to analyze props to minimize invalidating `AnimatedProps`.',
|
||||
expectedReleaseValue: true,
|
||||
purpose: 'release',
|
||||
},
|
||||
},
|
||||
fixVirtualizeListCollapseWindowSize: {
|
||||
defaultValue: false,
|
||||
metadata: {
|
||||
@@ -615,15 +597,6 @@ const definitions: FeatureFlagDefinitions = {
|
||||
purpose: 'experimentation',
|
||||
},
|
||||
},
|
||||
useInsertionEffectsForAnimations: {
|
||||
defaultValue: true,
|
||||
metadata: {
|
||||
description:
|
||||
'Changes construction of the animation graph to `useInsertionEffect` instead of `useLayoutEffect`.',
|
||||
expectedReleaseValue: true,
|
||||
purpose: 'release',
|
||||
},
|
||||
},
|
||||
useRefsForTextInputState: {
|
||||
defaultValue: false,
|
||||
metadata: {
|
||||
|
||||
@@ -1362,9 +1362,6 @@ describe('Native Animated', () => {
|
||||
|
||||
describe('Animated Components', () => {
|
||||
it('preserves current values on update and unmount', async () => {
|
||||
const {ReactNativeFeatureFlags} = importModules();
|
||||
ReactNativeFeatureFlags.override({enableAnimatedPropsMemo: () => true});
|
||||
|
||||
const {Animated} = importModules();
|
||||
|
||||
const opacity = new Animated.Value(0);
|
||||
@@ -1382,9 +1379,6 @@ describe('Native Animated', () => {
|
||||
});
|
||||
|
||||
it('restores defaults when receiving new animated values', async () => {
|
||||
const {ReactNativeFeatureFlags} = importModules();
|
||||
ReactNativeFeatureFlags.override({enableAnimatedPropsMemo: () => true});
|
||||
|
||||
const {Animated} = importModules();
|
||||
|
||||
const opacityA = new Animated.Value(0);
|
||||
@@ -1407,30 +1401,6 @@ describe('Native Animated', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should restore default values on prop updates only', async () => {
|
||||
const {ReactNativeFeatureFlags} = importModules();
|
||||
ReactNativeFeatureFlags.override({enableAnimatedPropsMemo: () => false});
|
||||
|
||||
const {Animated} = importModules();
|
||||
|
||||
const opacity = new Animated.Value(0);
|
||||
opacity.__makeNative();
|
||||
|
||||
const root = await create(<Animated.View style={{opacity}} />);
|
||||
expect(NativeAnimatedModule.restoreDefaultValues).not.toHaveBeenCalled();
|
||||
|
||||
await update(root, <Animated.View style={{opacity}} />);
|
||||
expect(NativeAnimatedModule.restoreDefaultValues).toHaveBeenCalledWith(
|
||||
expect.any(Number),
|
||||
);
|
||||
|
||||
await unmount(root);
|
||||
// Make sure it doesn't get called on unmount.
|
||||
expect(NativeAnimatedModule.restoreDefaultValues).toHaveBeenCalledTimes(
|
||||
1,
|
||||
);
|
||||
});
|
||||
|
||||
it('connects the native view on mount and disconnects on unmount', async () => {
|
||||
const {Animated} = importModules();
|
||||
|
||||
|
||||
@@ -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<<e1f16c89435be5abedd0a060ef036d95>>
|
||||
* @generated SignedSource<<811a054f0ada56d8cc1ed21c6930fc52>>
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
@@ -32,16 +32,13 @@ export type ReactNativeFeatureFlagsJsOnly = $ReadOnly<{
|
||||
animatedShouldUseSingleOp: Getter<boolean>,
|
||||
disableInteractionManager: Getter<boolean>,
|
||||
enableAccessToHostTreeInFabric: Getter<boolean>,
|
||||
enableAnimatedAllowlist: Getter<boolean>,
|
||||
enableAnimatedClearImmediateFix: Getter<boolean>,
|
||||
enableAnimatedPropsMemo: Getter<boolean>,
|
||||
fixVirtualizeListCollapseWindowSize: Getter<boolean>,
|
||||
isLayoutAnimationEnabled: Getter<boolean>,
|
||||
shouldSkipStateUpdatesForLoopingAnimations: Getter<boolean>,
|
||||
shouldUseAnimatedObjectForTransform: Getter<boolean>,
|
||||
shouldUseRemoveClippedSubviewsAsDefaultOnIOS: Getter<boolean>,
|
||||
shouldUseSetNativePropsInFabric: Getter<boolean>,
|
||||
useInsertionEffectsForAnimations: Getter<boolean>,
|
||||
useRefsForTextInputState: Getter<boolean>,
|
||||
}>;
|
||||
|
||||
@@ -123,21 +120,11 @@ export const disableInteractionManager: Getter<boolean> = createJavaScriptFlagGe
|
||||
*/
|
||||
export const enableAccessToHostTreeInFabric: Getter<boolean> = createJavaScriptFlagGetter('enableAccessToHostTreeInFabric', false);
|
||||
|
||||
/**
|
||||
* Enables Animated to skip non-allowlisted props and styles.
|
||||
*/
|
||||
export const enableAnimatedAllowlist: Getter<boolean> = createJavaScriptFlagGetter('enableAnimatedAllowlist', true);
|
||||
|
||||
/**
|
||||
* Enables an experimental to use the proper clearIntermediate instead of calling the wrong clearTimeout and canceling another timer.
|
||||
*/
|
||||
export const enableAnimatedClearImmediateFix: Getter<boolean> = createJavaScriptFlagGetter('enableAnimatedClearImmediateFix', true);
|
||||
|
||||
/**
|
||||
* Enables Animated to analyze props to minimize invalidating `AnimatedProps`.
|
||||
*/
|
||||
export const enableAnimatedPropsMemo: Getter<boolean> = createJavaScriptFlagGetter('enableAnimatedPropsMemo', true);
|
||||
|
||||
/**
|
||||
* Fixing an edge case where the current window size is not properly calculated with fast scrolling. Window size collapsed to 1 element even if windowSize more than the current amount of elements
|
||||
*/
|
||||
@@ -168,11 +155,6 @@ export const shouldUseRemoveClippedSubviewsAsDefaultOnIOS: Getter<boolean> = cre
|
||||
*/
|
||||
export const shouldUseSetNativePropsInFabric: Getter<boolean> = createJavaScriptFlagGetter('shouldUseSetNativePropsInFabric', true);
|
||||
|
||||
/**
|
||||
* Changes construction of the animation graph to `useInsertionEffect` instead of `useLayoutEffect`.
|
||||
*/
|
||||
export const useInsertionEffectsForAnimations: Getter<boolean> = createJavaScriptFlagGetter('useInsertionEffectsForAnimations', true);
|
||||
|
||||
/**
|
||||
* Enable a variant of TextInput that moves some state to refs to avoid unnecessary re-renders
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user