Skip to content

Commit

Permalink
Updated createAnimatedComponent to account for async rendering
Browse files Browse the repository at this point in the history
Reviewed By: sahrens

Differential Revision: D6149113

fbshipit-source-id: f28b597c1fe9280ca990fe72efc7b841665de957
  • Loading branch information
Brian Vaughn authored and facebook-github-bot committed Oct 31, 2017
1 parent 01b9419 commit 870f540
Showing 1 changed file with 42 additions and 35 deletions.
77 changes: 42 additions & 35 deletions Libraries/Animated/src/createAnimatedComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const ViewStylePropTypes = require('ViewStylePropTypes');
function createAnimatedComponent(Component: any): any {
class AnimatedComponent extends React.Component<Object> {
_component: any;
_invokeAnimatedPropsCallbackOnMount: boolean = false;
_prevComponent: any;
_propsAnimated: AnimatedProps;
_eventDetachers: Array<Function> = [];
Expand All @@ -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();
}
Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 870f540

Please sign in to comment.