mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Reduce re-rendering of Animated components in Fabric
Summary: The `isFabric` method used in createAnimatedComponent is unreliable (another reason in a long list of reasons why you should not duplicate this code elsewhere, and why we want to delete it ASAP). In particular, during the first render, the ref component has not been set yet, so we /cannot/ detect if the component is Fabric or non-Fabric and assume it's non-Fabric. In Fabric, this causes `collapsable` and `nativeID` values to change after the first render. To reduce this re-rendering, but not eliminate it for all components, I've introduced a flag that indicates if a component will /never/ be flattened. In particular, Image components, ScrollViews, Text cannot ever be flattened, so we should always pass `collapsable:false` and the same nativeID prop for those components. For Animated <View>s and other components, the re-rendering issue is still a problem in Fabric for now. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D25720322 fbshipit-source-id: fe3234d8ae974911a2b5f82e4f6a093216f43d4b
This commit is contained in:
committed by
Facebook GitHub Bot
parent
f002a5710f
commit
b8ca59cd74
@@ -17,9 +17,9 @@ const createAnimatedComponent = require('../createAnimatedComponent');
|
||||
|
||||
import type {AnimatedComponentType} from '../createAnimatedComponent';
|
||||
|
||||
module.exports = (createAnimatedComponent(
|
||||
(Image: $FlowFixMe),
|
||||
): AnimatedComponentType<
|
||||
module.exports = (createAnimatedComponent((Image: $FlowFixMe), {
|
||||
collapsable: false,
|
||||
}): AnimatedComponentType<
|
||||
React.ElementConfig<typeof Image>,
|
||||
React.ElementRef<typeof Image>,
|
||||
>);
|
||||
|
||||
@@ -24,9 +24,9 @@ const ScrollViewWithEventThrottle = React.forwardRef((props, ref) => (
|
||||
<ScrollView scrollEventThrottle={0.0001} {...props} ref={ref} />
|
||||
));
|
||||
|
||||
module.exports = (createAnimatedComponent(
|
||||
ScrollViewWithEventThrottle,
|
||||
): AnimatedComponentType<
|
||||
module.exports = (createAnimatedComponent(ScrollViewWithEventThrottle, {
|
||||
collapsable: false,
|
||||
}): AnimatedComponentType<
|
||||
React.ElementConfig<typeof ScrollView>,
|
||||
React.ElementRef<typeof ScrollView>,
|
||||
>);
|
||||
|
||||
@@ -17,9 +17,9 @@ const createAnimatedComponent = require('../createAnimatedComponent');
|
||||
|
||||
import type {AnimatedComponentType} from '../createAnimatedComponent';
|
||||
|
||||
module.exports = (createAnimatedComponent(
|
||||
(Text: $FlowFixMe),
|
||||
): AnimatedComponentType<
|
||||
module.exports = (createAnimatedComponent((Text: $FlowFixMe), {
|
||||
collapsable: false,
|
||||
}): AnimatedComponentType<
|
||||
React.ElementConfig<typeof Text>,
|
||||
React.ElementRef<typeof Text>,
|
||||
>);
|
||||
|
||||
@@ -17,7 +17,9 @@ const createAnimatedComponent = require('../createAnimatedComponent');
|
||||
|
||||
import type {AnimatedComponentType} from '../createAnimatedComponent';
|
||||
|
||||
module.exports = (createAnimatedComponent(View): AnimatedComponentType<
|
||||
module.exports = (createAnimatedComponent(View, {
|
||||
collapsable: true,
|
||||
}): AnimatedComponentType<
|
||||
React.ElementConfig<typeof View>,
|
||||
React.ElementRef<typeof View>,
|
||||
>);
|
||||
|
||||
@@ -37,8 +37,13 @@ export type AnimatedComponentType<
|
||||
Instance,
|
||||
>;
|
||||
|
||||
type AnimatedComponentOptions = {
|
||||
collapsable?: boolean,
|
||||
};
|
||||
|
||||
function createAnimatedComponent<Props: {+[string]: mixed, ...}, Instance>(
|
||||
Component: React.AbstractComponent<Props, Instance>,
|
||||
options?: AnimatedComponentOptions,
|
||||
): AnimatedComponentType<Props, Instance> {
|
||||
invariant(
|
||||
typeof Component !== 'function' ||
|
||||
@@ -79,6 +84,9 @@ function createAnimatedComponent<Props: {+[string]: mixed, ...}, Instance>(
|
||||
}
|
||||
|
||||
_isFabric = (): boolean => {
|
||||
// When called during the first render, `_component` is always null.
|
||||
// Therefore, even if a component is rendered in Fabric, we can't detect
|
||||
// that until ref is set, which happens sometime after the first render.
|
||||
if (this._component == null) {
|
||||
return false;
|
||||
}
|
||||
@@ -213,23 +221,28 @@ function createAnimatedComponent<Props: {+[string]: mixed, ...}, Instance>(
|
||||
const {style: passthruStyle = {}, ...passthruProps} =
|
||||
this.props.passthroughAnimatedPropExplicitValues || {};
|
||||
const mergedStyle = {...style, ...passthruStyle};
|
||||
const forceNativeId =
|
||||
props.collapsable ??
|
||||
(this._propsAnimated.__isNative ||
|
||||
this._isFabric() ||
|
||||
options?.collapsable === false);
|
||||
// The native driver updates views directly through the UI thread so we
|
||||
// have to make sure the view doesn't get optimized away because it cannot
|
||||
// go through the NativeViewHierarchyManager since it operates on the shadow
|
||||
// thread. TODO: T68258846
|
||||
const collapsableProps = forceNativeId
|
||||
? {
|
||||
nativeID: props.nativeID ?? 'animatedComponent',
|
||||
collapsable: false,
|
||||
}
|
||||
: {};
|
||||
return (
|
||||
<Component
|
||||
{...props}
|
||||
{...passthruProps}
|
||||
{...collapsableProps}
|
||||
style={mergedStyle}
|
||||
ref={this._setComponentRef}
|
||||
nativeID={
|
||||
props.nativeID ??
|
||||
(this._isFabric() ? 'animatedComponent' : undefined)
|
||||
} /* TODO: T68258846. */
|
||||
// The native driver updates views directly through the UI thread so we
|
||||
// have to make sure the view doesn't get optimized away because it cannot
|
||||
// go through the NativeViewHierarchyManager since it operates on the shadow
|
||||
// thread.
|
||||
collapsable={
|
||||
this._propsAnimated.__isNative ? false : props.collapsable
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user