From 870f540336da998f4c0fe26f9061f201785a6110 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 31 Oct 2017 11:08:19 -0700 Subject: [PATCH] Updated createAnimatedComponent to account for async rendering Reviewed By: sahrens Differential Revision: D6149113 fbshipit-source-id: f28b597c1fe9280ca990fe72efc7b841665de957 --- .../Animated/src/createAnimatedComponent.js | 77 ++++++++++--------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/Libraries/Animated/src/createAnimatedComponent.js b/Libraries/Animated/src/createAnimatedComponent.js index f5e5200ba..7126deaf2 100644 --- a/Libraries/Animated/src/createAnimatedComponent.js +++ b/Libraries/Animated/src/createAnimatedComponent.js @@ -20,6 +20,7 @@ const ViewStylePropTypes = require('ViewStylePropTypes'); function createAnimatedComponent(Component: any): any { class AnimatedComponent extends React.Component { _component: any; + _invokeAnimatedPropsCallbackOnMount: boolean = false; _prevComponent: any; _propsAnimated: AnimatedProps; _eventDetachers: Array = []; @@ -46,6 +47,11 @@ function createAnimatedComponent(Component: any): any { } componentDidMount() { + if (this._invokeAnimatedPropsCallbackOnMount) { + this._invokeAnimatedPropsCallbackOnMount = false; + this._animatedPropsCallback(); + } + this._propsAnimated.setNativeView(this._component); this._attachNativeEvents(); } @@ -71,37 +77,44 @@ function createAnimatedComponent(Component: any): any { this._eventDetachers = []; } + // The system is best designed when setNativeProps is implemented. It is + // able to avoid re-rendering and directly set the attributes that changed. + // However, setNativeProps can only be implemented on leaf native + // components. If you want to animate a composite component, you need to + // re-render it. In this case, we have a fallback that uses forceUpdate. + _animatedPropsCallback = () => { + if (this._component == null) { + // AnimatedProps is created in will-mount because it's used in render. + // But this callback may be invoked before mount in async mode, + // In which case we should defer the setNativeProps() call. + // React may throw away uncommitted work in async mode, + // So a deferred call won't always be invoked. + this._invokeAnimatedPropsCallbackOnMount = true; + } else if ( + AnimatedComponent.__skipSetNativeProps_FOR_TESTS_ONLY || + typeof this._component.setNativeProps !== 'function' + ) { + this.forceUpdate(); + } else if (!this._propsAnimated.__isNative) { + this._component.setNativeProps( + this._propsAnimated.__getAnimatedValue(), + ); + } else { + throw new Error( + 'Attempting to run JS driven animation on animated ' + + 'node that has been moved to "native" earlier by starting an ' + + 'animation with `useNativeDriver: true`', + ); + } + }; + _attachProps(nextProps) { const oldPropsAnimated = this._propsAnimated; - // The system is best designed when setNativeProps is implemented. It is - // able to avoid re-rendering and directly set the attributes that - // changed. However, setNativeProps can only be implemented on leaf - // native components. If you want to animate a composite component, you - // need to re-render it. In this case, we have a fallback that uses - // forceUpdate. - const callback = () => { - if ( - !AnimatedComponent.__skipSetNativeProps_FOR_TESTS_ONLY && - this._component.setNativeProps - ) { - if (!this._propsAnimated.__isNative) { - this._component.setNativeProps( - this._propsAnimated.__getAnimatedValue(), - ); - } else { - throw new Error( - 'Attempting to run JS driven animation on animated ' + - 'node that has been moved to "native" earlier by starting an ' + - 'animation with `useNativeDriver: true`', - ); - } - } else { - this.forceUpdate(); - } - }; - - this._propsAnimated = new AnimatedProps(nextProps, callback); + this._propsAnimated = new AnimatedProps( + nextProps, + this._animatedPropsCallback, + ); // When you call detach, it removes the element from the parent list // of children. If it goes to 0, then the parent also detaches itself @@ -157,13 +170,7 @@ function createAnimatedComponent(Component: any): any { } } - // ReactNative `View.propTypes` have been deprecated in favor of - // `ViewPropTypes`. In their place a temporary getter has been added with a - // deprecated warning message. Avoid triggering that warning here by using - // temporary workaround, __propTypesSecretDontUseThesePlease. - // TODO (bvaughn) Revert this particular change any time after April 1 - const propTypes = - Component.__propTypesSecretDontUseThesePlease || Component.propTypes; + const propTypes = Component.propTypes; AnimatedComponent.propTypes = { style: function(props, propName, componentName) {