Skip to content

Commit 9c3e6ae

Browse files
sahrensfacebook-github-bot
authored andcommitted
Fix Animated.event attach/detach on component re-render
Summary: Need to make sure `detach` happens on the old `scrollableNode` before it's unmounted and that `attach` happens on the new `scrollableNode` after it's mounted. This should also be more performant because the detach step no longer requires iterating through all the props, most of which are not animated, and we filter out unneeded updates if props or ref haven't changed. = Test Plan = Hook up native onscroll events in `FlatListExample` and toggle "debug" - before, the events would stop working because they would try to attach to the old unmounted node, but with this diff it keeps working as expected. Reviewed By: fkgozali Differential Revision: D4687186 fbshipit-source-id: 313a7964d4614190308490a51fc4f56abb6690f8
1 parent f48b54b commit 9c3e6ae

File tree

1 file changed

+35
-39
lines changed

1 file changed

+35
-39
lines changed

Libraries/Animated/src/AnimatedImplementation.js

+35-39
Original file line numberDiff line numberDiff line change
@@ -1510,9 +1510,9 @@ class AnimatedStyle extends AnimatedWithChildren {
15101510
15111511
// Recursively get values for nested styles (like iOS's shadowOffset)
15121512
__walkStyleAndGetValues(style) {
1513-
let updatedStyle = {};
1514-
for (let key in style) {
1515-
let value = style[key];
1513+
const updatedStyle = {};
1514+
for (const key in style) {
1515+
const value = style[key];
15161516
if (value instanceof Animated) {
15171517
if (!value.__isNative) {
15181518
// We cannot use value of natively driven nodes this way as the value we have access from
@@ -1535,9 +1535,9 @@ class AnimatedStyle extends AnimatedWithChildren {
15351535
15361536
// Recursively get animated values for nested styles (like iOS's shadowOffset)
15371537
__walkStyleAndGetAnimatedValues(style) {
1538-
let updatedStyle = {};
1539-
for (let key in style) {
1540-
let value = style[key];
1538+
const updatedStyle = {};
1539+
for (const key in style) {
1540+
const value = style[key];
15411541
if (value instanceof Animated) {
15421542
updatedStyle[key] = value.__getAnimatedValue();
15431543
} else if (value && !Array.isArray(value) && typeof value === 'object') {
@@ -1690,7 +1690,9 @@ class AnimatedProps extends Animated {
16901690
}
16911691
16921692
setNativeView(animatedView: any): void {
1693-
invariant(this._animatedView === undefined, 'Animated view already set.');
1693+
if (this._animatedView === animatedView) {
1694+
return;
1695+
}
16941696
this._animatedView = animatedView;
16951697
if (this.__isNative) {
16961698
this.__connectAnimatedView();
@@ -1729,7 +1731,9 @@ class AnimatedProps extends Animated {
17291731
function createAnimatedComponent(Component: any): any {
17301732
class AnimatedComponent extends React.Component {
17311733
_component: any;
1734+
_prevComponent: any;
17321735
_propsAnimated: AnimatedProps;
1736+
_eventDetachers: Array<Function> = [];
17331737
_setComponentRef: Function;
17341738
17351739
constructor(props: Object) {
@@ -1739,7 +1743,7 @@ function createAnimatedComponent(Component: any): any {
17391743
17401744
componentWillUnmount() {
17411745
this._propsAnimated && this._propsAnimated.__detach();
1742-
this._detachNativeEvents(this.props);
1746+
this._detachNativeEvents();
17431747
}
17441748
17451749
setNativeProps(props) {
@@ -1752,42 +1756,28 @@ function createAnimatedComponent(Component: any): any {
17521756
17531757
componentDidMount() {
17541758
this._propsAnimated.setNativeView(this._component);
1755-
1756-
this._attachNativeEvents(this.props);
1759+
this._attachNativeEvents();
17571760
}
17581761
1759-
_attachNativeEvents(newProps) {
1760-
if (newProps !== this.props) {
1761-
this._detachNativeEvents(this.props);
1762-
}
1763-
1762+
_attachNativeEvents() {
17641763
// Make sure to get the scrollable node for components that implement
17651764
// `ScrollResponder.Mixin`.
1766-
const ref = this._component.getScrollableNode ?
1765+
const scrollableNode = this._component.getScrollableNode ?
17671766
this._component.getScrollableNode() :
17681767
this._component;
17691768
1770-
for (const key in newProps) {
1771-
const prop = newProps[key];
1769+
for (const key in this.props) {
1770+
const prop = this.props[key];
17721771
if (prop instanceof AnimatedEvent && prop.__isNative) {
1773-
prop.__attach(ref, key);
1772+
prop.__attach(scrollableNode, key);
1773+
this._eventDetachers.push(() => prop.__detach(scrollableNode, key));
17741774
}
17751775
}
17761776
}
17771777
1778-
_detachNativeEvents(props) {
1779-
// Make sure to get the scrollable node for components that implement
1780-
// `ScrollResponder.Mixin`.
1781-
const ref = this._component.getScrollableNode ?
1782-
this._component.getScrollableNode() :
1783-
this._component;
1784-
1785-
for (const key in props) {
1786-
const prop = props[key];
1787-
if (prop instanceof AnimatedEvent && prop.__isNative) {
1788-
prop.__detach(ref, key);
1789-
}
1790-
}
1778+
_detachNativeEvents() {
1779+
this._eventDetachers.forEach(remove => remove());
1780+
this._eventDetachers = [];
17911781
}
17921782
17931783
_attachProps(nextProps) {
@@ -1820,10 +1810,6 @@ function createAnimatedComponent(Component: any): any {
18201810
callback,
18211811
);
18221812
1823-
if (this._component) {
1824-
this._propsAnimated.setNativeView(this._component);
1825-
}
1826-
18271813
// When you call detach, it removes the element from the parent list
18281814
// of children. If it goes to 0, then the parent also detaches itself
18291815
// and so on.
@@ -1835,9 +1821,18 @@ function createAnimatedComponent(Component: any): any {
18351821
oldPropsAnimated && oldPropsAnimated.__detach();
18361822
}
18371823
1838-
componentWillReceiveProps(nextProps) {
1839-
this._attachProps(nextProps);
1840-
this._attachNativeEvents(nextProps);
1824+
componentWillReceiveProps(newProps) {
1825+
this._attachProps(newProps);
1826+
}
1827+
1828+
componentDidUpdate(prevProps) {
1829+
if (this._component !== this._prevComponent) {
1830+
this._propsAnimated.setNativeView(this._component);
1831+
}
1832+
if (this._component !== this._prevComponent || prevProps !== this.props) {
1833+
this._detachNativeEvents();
1834+
this._attachNativeEvents();
1835+
}
18411836
}
18421837
18431838
render() {
@@ -1850,6 +1845,7 @@ function createAnimatedComponent(Component: any): any {
18501845
}
18511846
18521847
_setComponentRef(c) {
1848+
this._prevComponent = this._component;
18531849
this._component = c;
18541850
}
18551851

0 commit comments

Comments
 (0)