Skip to content

Commit a041951

Browse files
genkikondofacebook-github-bot
authored andcommitted
Only use value of natively driven nodes on initial render
Summary: D36612758 (40f4c66) attempted to remove the check that the animated node was native when fetching animated prop values for render, in order to fix an issue that arises when a node is converted to native before the initial call to render to mount the component, where the initial value is not applied. However, this causes issues for cases where we call setValue on an animated node soon after render, such as on componentDidUpdate. setValue first updates the animated node value on JS side, then calls the native API setAnimatedNodeValue. The problem is that the next render will then use these updated values in the style props (because we removed the check for native in D36612758 (40f4c66)), resulting in a diff in props. Since Animated and Fabric aren't synchronized, there's a race condition between SurfaceMountingManager.updateProps and NativeAnimatedNodesManager.runUpdates To allow proper rendering of initial values of native animated nodes, and mitigate the issue when calling setValue on an animated node soon after render, during initial render we use the initial values of both native and non-native driven nodes, and on subsequent renders we only use the initial values of non-native driven nodes. An alternative considered was to add internal state to the nodes themselves (AnimatedProps, AnimatedStyle), but keeping it in sync with the component state is not easy/clean as AnimatedProps can be recreated and reattached at any time for a mounted component. Changelog: [Internal][Fixed] - Only use value of natively driven nodes on initial render Reviewed By: JoshuaGross Differential Revision: D36902220 fbshipit-source-id: c20f711aa97d18a2ed549b5f90c6296bf19bb327
1 parent 11141b8 commit a041951

File tree

3 files changed

+23
-8
lines changed

3 files changed

+23
-8
lines changed

Libraries/Animated/createAnimatedComponent.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ function createAnimatedComponent<Props: {+[string]: mixed, ...}, Instance>(
5555
_prevComponent: any;
5656
_propsAnimated: AnimatedProps;
5757
_eventDetachers: Array<Function> = [];
58+
_isInitialRender: boolean = true;
5859

5960
// Only to be used in this file, and only in Fabric.
6061
_animatedComponentId: string = `${animatedComponentNextId++}:animatedComponent`;
@@ -200,7 +201,8 @@ function createAnimatedComponent<Props: {+[string]: mixed, ...}, Instance>(
200201
});
201202

202203
render() {
203-
const {style = {}, ...props} = this._propsAnimated.__getValue() || {};
204+
const {style = {}, ...props} =
205+
this._propsAnimated.__getValue(this._isInitialRender) || {};
204206
const {style: passthruStyle = {}, ...passthruProps} =
205207
this.props.passthroughAnimatedPropExplicitValues || {};
206208
const mergedStyle = {...style, ...passthruStyle};
@@ -232,6 +234,7 @@ function createAnimatedComponent<Props: {+[string]: mixed, ...}, Instance>(
232234
this._propsAnimated.setNativeView(this._component);
233235
this._attachNativeEvents();
234236
this._markUpdateComplete();
237+
this._isInitialRender = false;
235238
}
236239

237240
UNSAFE_componentWillReceiveProps(newProps: any) {

Libraries/Animated/nodes/AnimatedProps.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,19 @@ class AnimatedProps extends AnimatedNode {
3636
this._callback = callback;
3737
}
3838

39-
__getValue(): Object {
39+
__getValue(isInitialRender: boolean = true): Object {
4040
const props: {[string]: any | ((...args: any) => void)} = {};
4141
for (const key in this._props) {
4242
const value = this._props[key];
4343
if (value instanceof AnimatedNode) {
44-
props[key] = value.__getValue();
44+
// During initial render we want to use the initial value of both natively and non-natively
45+
// driven nodes. On subsequent renders, we cannot use the value of natively driven nodes
46+
// as they may not be up to date.
47+
if (value instanceof AnimatedStyle) {
48+
props[key] = value.__getValue(isInitialRender);
49+
} else if (isInitialRender || !value.__isNative) {
50+
props[key] = value.__getValue();
51+
}
4552
} else if (value instanceof AnimatedEvent) {
4653
props[key] = value.__getHandler();
4754
} else {

Libraries/Animated/nodes/AnimatedStyle.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,29 @@ class AnimatedStyle extends AnimatedWithChildren {
3434
}
3535

3636
// Recursively get values for nested styles (like iOS's shadowOffset)
37-
_walkStyleAndGetValues(style: any) {
37+
_walkStyleAndGetValues(style: any, isInitialRender: boolean) {
3838
const updatedStyle: {[string]: any | {...}} = {};
3939
for (const key in style) {
4040
const value = style[key];
4141
if (value instanceof AnimatedNode) {
42-
updatedStyle[key] = value.__getValue();
42+
// During initial render we want to use the initial value of both natively and non-natively
43+
// driven nodes. On subsequent renders, we cannot use the value of natively driven nodes
44+
// as they may not be up to date.
45+
if (isInitialRender || !value.__isNative) {
46+
updatedStyle[key] = value.__getValue();
47+
}
4348
} else if (value && !Array.isArray(value) && typeof value === 'object') {
4449
// Support animating nested values (for example: shadowOffset.height)
45-
updatedStyle[key] = this._walkStyleAndGetValues(value);
50+
updatedStyle[key] = this._walkStyleAndGetValues(value, isInitialRender);
4651
} else {
4752
updatedStyle[key] = value;
4853
}
4954
}
5055
return updatedStyle;
5156
}
5257

53-
__getValue(): Object {
54-
return this._walkStyleAndGetValues(this._style);
58+
__getValue(isInitialRender: boolean = true): Object {
59+
return this._walkStyleAndGetValues(this._style, isInitialRender);
5560
}
5661

5762
// Recursively get animated values for nested styles (like iOS's shadowOffset)

0 commit comments

Comments
 (0)