Skip to content

Commit b3189ca

Browse files
Bartlomiej Bloniarzfacebook-github-bot
authored andcommitted
Add synchronous updates to the AnimatedPropsRegistry (#54878)
Summary: This diff changes the AnimatedPropsRegistry to also contain updates that go through the synchronous props update fast-path. Reviewed By: zeyap Differential Revision: D89042934
1 parent 5e5e59d commit b3189ca

File tree

7 files changed

+222
-109
lines changed

7 files changed

+222
-109
lines changed

packages/react-native/Libraries/Animated/__tests__/AnimatedBackend-itest.js

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,15 +191,126 @@ test('animate layout props and rerender', () => {
191191
_setWidth(200);
192192
});
193193

194+
// TODO: getFabricUpdateProps is not working with the cloneMutliple method
195+
// expect(Fantom.unstable_getFabricUpdateProps(viewElement).height).toBe(50);
196+
expect(root.getRenderedOutput({props: ['height', 'width']}).toJSX()).toEqual(
197+
<rn-view height="50.000000" width="200.000000" />,
198+
);
199+
200+
Fantom.unstable_produceFramesForDuration(500);
201+
194202
// TODO: this shouldn't be neccessary since animation should be stopped after duration
195203
Fantom.runTask(() => {
196204
_heightAnimation?.stop();
197205
});
198206

207+
expect(root.getRenderedOutput({props: ['height', 'width']}).toJSX()).toEqual(
208+
<rn-view height="100.000000" width="200.000000" />,
209+
);
210+
211+
Fantom.runTask(() => {
212+
_setWidth(300);
213+
});
214+
215+
expect(root.getRenderedOutput({props: ['height', 'width']}).toJSX()).toEqual(
216+
<rn-view height="100.000000" width="300.000000" />,
217+
);
218+
});
219+
220+
test('animate non-layout props and rerender', () => {
221+
const viewRef = createRef<HostInstance>();
222+
223+
let _animatedOpacity;
224+
let _opacityAnimation;
225+
let _setWidth;
226+
227+
function MyApp() {
228+
const animatedOpacity = useAnimatedValue(0);
229+
const [width, setWidth] = useState(100);
230+
_animatedOpacity = animatedOpacity;
231+
_setWidth = setWidth;
232+
return (
233+
<Animated.View
234+
ref={viewRef}
235+
style={[
236+
{
237+
width: width,
238+
opacity: animatedOpacity,
239+
},
240+
]}
241+
/>
242+
);
243+
}
244+
245+
const root = Fantom.createRoot();
246+
247+
Fantom.runTask(() => {
248+
root.render(<MyApp />);
249+
});
250+
251+
const viewElement = ensureInstance(viewRef.current, ReactNativeElement);
252+
253+
Fantom.runTask(() => {
254+
_opacityAnimation = Animated.timing(_animatedOpacity, {
255+
toValue: 0.5,
256+
duration: 1000,
257+
useNativeDriver: true,
258+
}).start();
259+
});
260+
261+
Fantom.unstable_produceFramesForDuration(500);
262+
263+
// TODO: rendered output should be <rn-view opacity="0,5" width="100.000000" /> at this point, but synchronous updates are not captured by fantom
264+
expect(root.getRenderedOutput({props: ['width']}).toJSX()).toEqual(
265+
<rn-view width="100.000000" />,
266+
);
267+
268+
expect(
269+
Fantom.unstable_getDirectManipulationProps(viewElement).opacity,
270+
).toBeCloseTo(0.25, 0.001);
271+
272+
// Re-render
273+
Fantom.runTask(() => {
274+
_setWidth(150);
275+
});
276+
277+
expect(root.getRenderedOutput({props: ['opacity', 'width']}).toJSX()).toEqual(
278+
<rn-view opacity="0.25" width="150.000000" />,
279+
);
280+
281+
Fantom.runTask(() => {
282+
_setWidth(200);
283+
});
284+
199285
// TODO: getFabricUpdateProps is not working with the cloneMutliple method
200286
// expect(Fantom.unstable_getFabricUpdateProps(viewElement).height).toBe(50);
201-
expect(root.getRenderedOutput({props: ['height', 'width']}).toJSX()).toEqual(
202-
<rn-view height="50.000000" width="200.000000" />,
287+
expect(root.getRenderedOutput({props: ['opacity', 'width']}).toJSX()).toEqual(
288+
<rn-view opacity="0.25" width="200.000000" />,
289+
);
290+
291+
Fantom.unstable_produceFramesForDuration(500);
292+
293+
// TODO: this shouldn't be neccessary since animation should be stopped after duration
294+
Fantom.runTask(() => {
295+
_opacityAnimation?.stop();
296+
});
297+
298+
// TODO: T246961305 rendered output should be <rn-view opacity="1" /> at this point
299+
expect(root.getRenderedOutput({props: ['width']}).toJSX()).toEqual(
300+
<rn-view width="200.000000" />,
301+
);
302+
303+
expect(Fantom.unstable_getDirectManipulationProps(viewElement).opacity).toBe(
304+
0.5,
305+
);
306+
307+
// Re-render
308+
Fantom.runTask(() => {
309+
_setWidth(300);
310+
});
311+
312+
expect(root.getRenderedOutput({props: ['opacity', 'width']}).toJSX()).toEqual(
313+
<rn-view opacity="0.5" width="300.000000" />,
203314
);
204315
});
205316

packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,33 +1007,33 @@ AnimationMutations NativeAnimatedNodesManager::pullAnimationMutations() {
10071007
}
10081008
}
10091009

1010-
for (auto& [tag, props] : updateViewPropsDirect_) {
1011-
auto weakFamily = tagToShadowNodeFamily_[tag];
1012-
1013-
if (auto family = weakFamily.lock()) {
1014-
propsBuilder.storeDynamic(props);
1015-
mutations.batch.push_back(
1016-
AnimationMutation{
1017-
.tag = tag,
1018-
.family = family,
1019-
.props = propsBuilder.get(),
1020-
});
1021-
}
1022-
containsChange = true;
1023-
}
10241010
{
10251011
std::lock_guard<std::mutex> lock(tagToShadowNodeFamilyMutex_);
1012+
for (auto& [tag, props] : updateViewPropsDirect_) {
1013+
auto weakFamily = tagToShadowNodeFamily_[tag];
1014+
1015+
if (auto family = weakFamily.lock()) {
1016+
propsBuilder.storeDynamic(props);
1017+
mutations.batch.push_back(
1018+
AnimationMutation{
1019+
.tag = tag,
1020+
.family = family,
1021+
.props = propsBuilder.get(),
1022+
});
1023+
}
1024+
containsChange = true;
1025+
}
10261026
for (auto& [tag, props] : updateViewProps_) {
10271027
auto weakFamily = tagToShadowNodeFamily_[tag];
10281028

10291029
if (auto family = weakFamily.lock()) {
10301030
propsBuilder.storeDynamic(props);
1031-
mutations.hasLayoutUpdates = true;
10321031
mutations.batch.push_back(
10331032
AnimationMutation{
10341033
.tag = tag,
10351034
.family = family,
10361035
.props = propsBuilder.get(),
1036+
.hasLayoutUpdates = true,
10371037
});
10381038
}
10391039
containsChange = true;
@@ -1068,36 +1068,38 @@ AnimationMutations NativeAnimatedNodesManager::pullAnimationMutations() {
10681068

10691069
isEventAnimationInProgress_ = false;
10701070

1071-
for (auto& [tag, props] : updateViewPropsDirect_) {
1072-
auto weakFamily = tagToShadowNodeFamily_[tag];
1073-
1074-
if (auto family = weakFamily.lock()) {
1075-
propsBuilder.storeDynamic(props);
1076-
mutations.batch.push_back(
1077-
AnimationMutation{
1078-
.tag = tag,
1079-
.family = family,
1080-
.props = propsBuilder.get(),
1081-
});
1082-
}
1083-
}
10841071
{
10851072
std::lock_guard<std::mutex> lock(tagToShadowNodeFamilyMutex_);
1073+
for (auto& [tag, props] : updateViewPropsDirect_) {
1074+
auto weakFamily = tagToShadowNodeFamily_[tag];
1075+
1076+
if (auto family = weakFamily.lock()) {
1077+
propsBuilder.storeDynamic(props);
1078+
mutations.batch.push_back(
1079+
AnimationMutation{
1080+
.tag = tag,
1081+
.family = family,
1082+
.props = propsBuilder.get(),
1083+
});
1084+
}
1085+
}
10861086
for (auto& [tag, props] : updateViewProps_) {
10871087
auto weakFamily = tagToShadowNodeFamily_[tag];
10881088

10891089
if (auto family = weakFamily.lock()) {
10901090
propsBuilder.storeDynamic(props);
1091-
mutations.hasLayoutUpdates = true;
10921091
mutations.batch.push_back(
10931092
AnimationMutation{
10941093
.tag = tag,
10951094
.family = family,
10961095
.props = propsBuilder.get(),
1096+
.hasLayoutUpdates = true,
10971097
});
10981098
}
10991099
}
11001100
}
1101+
updateViewProps_.clear();
1102+
updateViewPropsDirect_.clear();
11011103
}
11021104
} else {
11031105
// There is no active animation. Stop the render callback.

packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.cpp

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -145,28 +145,30 @@ NativeAnimatedNodesManagerProvider::getOrCreate(
145145

146146
uiManager->setNativeAnimatedDelegate(nativeAnimatedDelegate_);
147147

148-
animatedMountingOverrideDelegate_ =
149-
std::make_shared<AnimatedMountingOverrideDelegate>(
150-
*nativeAnimatedNodesManager_, *scheduler);
151-
152-
// Register on existing surfaces
153-
uiManager->getShadowTreeRegistry().enumerate(
154-
[animatedMountingOverrideDelegate =
155-
std::weak_ptr<const AnimatedMountingOverrideDelegate>(
156-
animatedMountingOverrideDelegate_)](
157-
const ShadowTree& shadowTree, bool& /*stop*/) {
158-
shadowTree.getMountingCoordinator()->setMountingOverrideDelegate(
159-
animatedMountingOverrideDelegate);
160-
});
161-
// Register on surfaces started in the future
162-
uiManager->setOnSurfaceStartCallback(
163-
[animatedMountingOverrideDelegate =
164-
std::weak_ptr<const AnimatedMountingOverrideDelegate>(
165-
animatedMountingOverrideDelegate_)](
166-
const ShadowTree& shadowTree) {
167-
shadowTree.getMountingCoordinator()->setMountingOverrideDelegate(
168-
animatedMountingOverrideDelegate);
169-
});
148+
if (!ReactNativeFeatureFlags::useSharedAnimatedBackend()) {
149+
animatedMountingOverrideDelegate_ =
150+
std::make_shared<AnimatedMountingOverrideDelegate>(
151+
*nativeAnimatedNodesManager_, *scheduler);
152+
153+
// Register on existing surfaces
154+
uiManager->getShadowTreeRegistry().enumerate(
155+
[animatedMountingOverrideDelegate =
156+
std::weak_ptr<const AnimatedMountingOverrideDelegate>(
157+
animatedMountingOverrideDelegate_)](
158+
const ShadowTree& shadowTree, bool& /*stop*/) {
159+
shadowTree.getMountingCoordinator()->setMountingOverrideDelegate(
160+
animatedMountingOverrideDelegate);
161+
});
162+
// Register on surfaces started in the future
163+
uiManager->setOnSurfaceStartCallback(
164+
[animatedMountingOverrideDelegate =
165+
std::weak_ptr<const AnimatedMountingOverrideDelegate>(
166+
animatedMountingOverrideDelegate_)](
167+
const ShadowTree& shadowTree) {
168+
shadowTree.getMountingCoordinator()->setMountingOverrideDelegate(
169+
animatedMountingOverrideDelegate);
170+
});
171+
}
170172
}
171173
return nativeAnimatedNodesManager_;
172174
}

packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ AnimatedPropsRegistry::getMap(SurfaceId surfaceId) {
7070
map.insert_or_assign(tag, std::move(propsSnapshot));
7171
} else {
7272
auto& currentSnapshot = currentIt->second;
73+
if (propsSnapshot->rawProps) {
74+
if (currentSnapshot->rawProps) {
75+
currentSnapshot->rawProps->merge_patch(*propsSnapshot->rawProps);
76+
} else {
77+
currentSnapshot->rawProps = std::move(propsSnapshot->rawProps);
78+
}
79+
}
7380
for (auto& propName : propsSnapshot->propNames) {
7481
currentSnapshot->propNames.insert(propName);
7582
updateProp(propName, currentSnapshot->props, *propsSnapshot);

packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ struct SurfaceContext {
3030
struct SurfaceUpdates {
3131
std::unordered_set<const ShadowNodeFamily *> families;
3232
std::unordered_map<Tag, AnimatedProps> propsMap;
33+
bool hasLayoutUpdates{false};
3334
};
3435

3536
using SnapshotMap = std::unordered_map<Tag, std::unique_ptr<PropsSnapshot>>;

0 commit comments

Comments
 (0)