diff --git a/Libraries/Animated/createAnimatedComponent.js b/Libraries/Animated/createAnimatedComponent.js index a59b5fd4fd3877..c9dbe808ed713f 100644 --- a/Libraries/Animated/createAnimatedComponent.js +++ b/Libraries/Animated/createAnimatedComponent.js @@ -55,7 +55,7 @@ function createAnimatedComponent( _prevComponent: any; _propsAnimated: AnimatedProps; _eventDetachers: Array = []; - _isInitialRender: boolean = true; + _initialAnimatedProps: Object; // Only to be used in this file, and only in Fabric. _animatedComponentId: string = `${animatedComponentNextId++}:animatedComponent`; @@ -201,12 +201,17 @@ function createAnimatedComponent( }); render() { - const {style = {}, ...props} = - this._propsAnimated.__getValue(this._isInitialRender) || {}; + const animatedProps = + this._propsAnimated.__getValue(this._initialAnimatedProps) || {}; + const {style = {}, ...props} = animatedProps; const {style: passthruStyle = {}, ...passthruProps} = this.props.passthroughAnimatedPropExplicitValues || {}; const mergedStyle = {...style, ...passthruStyle}; + if (!this._initialAnimatedProps) { + this._initialAnimatedProps = animatedProps; + } + // Force `collapsable` to be false so that native view is not flattened. // Flattened views cannot be accurately referenced by a native driver. return ( @@ -234,7 +239,6 @@ function createAnimatedComponent( this._propsAnimated.setNativeView(this._component); this._attachNativeEvents(); this._markUpdateComplete(); - this._isInitialRender = false; } UNSAFE_componentWillReceiveProps(newProps: any) { diff --git a/Libraries/Animated/nodes/AnimatedProps.js b/Libraries/Animated/nodes/AnimatedProps.js index ead32545bc8e64..d4ed70da8d6e9c 100644 --- a/Libraries/Animated/nodes/AnimatedProps.js +++ b/Libraries/Animated/nodes/AnimatedProps.js @@ -36,18 +36,23 @@ class AnimatedProps extends AnimatedNode { this._callback = callback; } - __getValue(isInitialRender: boolean = true): Object { + __getValue(initialProps: ?Object): Object { const props: {[string]: any | ((...args: any) => void)} = {}; for (const key in this._props) { const value = this._props[key]; if (value instanceof AnimatedNode) { // During initial render we want to use the initial value of both natively and non-natively // driven nodes. On subsequent renders, we cannot use the value of natively driven nodes - // as they may not be up to date. + // as they may not be up to date, so we use the initial value to ensure that values of + // native animated nodes do not impact rerenders. if (value instanceof AnimatedStyle) { - props[key] = value.__getValue(isInitialRender); - } else if (isInitialRender || !value.__isNative) { + props[key] = value.__getValue( + initialProps ? initialProps.style : null, + ); + } else if (!initialProps || !value.__isNative) { props[key] = value.__getValue(); + } else if (initialProps.hasOwnProperty(key)) { + props[key] = initialProps[key]; } } else if (value instanceof AnimatedEvent) { props[key] = value.__getHandler(); @@ -55,6 +60,7 @@ class AnimatedProps extends AnimatedNode { props[key] = value; } } + return props; } diff --git a/Libraries/Animated/nodes/AnimatedStyle.js b/Libraries/Animated/nodes/AnimatedStyle.js index 79df1468d253ab..c64706a81585ac 100644 --- a/Libraries/Animated/nodes/AnimatedStyle.js +++ b/Libraries/Animated/nodes/AnimatedStyle.js @@ -34,20 +34,23 @@ class AnimatedStyle extends AnimatedWithChildren { } // Recursively get values for nested styles (like iOS's shadowOffset) - _walkStyleAndGetValues(style: any, isInitialRender: boolean) { + _walkStyleAndGetValues(style: any, initialStyle: ?Object) { const updatedStyle: {[string]: any | {...}} = {}; for (const key in style) { const value = style[key]; if (value instanceof AnimatedNode) { // During initial render we want to use the initial value of both natively and non-natively // driven nodes. On subsequent renders, we cannot use the value of natively driven nodes - // as they may not be up to date. - if (isInitialRender || !value.__isNative) { + // as they may not be up to date, so we use the initial value to ensure that values of + // native animated nodes do not impact rerenders. + if (!initialStyle || !value.__isNative) { updatedStyle[key] = value.__getValue(); + } else if (initialStyle.hasOwnProperty(key)) { + updatedStyle[key] = initialStyle[key]; } } else if (value && !Array.isArray(value) && typeof value === 'object') { // Support animating nested values (for example: shadowOffset.height) - updatedStyle[key] = this._walkStyleAndGetValues(value, isInitialRender); + updatedStyle[key] = this._walkStyleAndGetValues(value, initialStyle); } else { updatedStyle[key] = value; } @@ -55,8 +58,8 @@ class AnimatedStyle extends AnimatedWithChildren { return updatedStyle; } - __getValue(isInitialRender: boolean = true): Object { - return this._walkStyleAndGetValues(this._style, isInitialRender); + __getValue(initialStyle: ?Object): Object { + return this._walkStyleAndGetValues(this._style, initialStyle); } // Recursively get animated values for nested styles (like iOS's shadowOffset)