Skip to content

Commit dbdf39a

Browse files
sammy-SCfacebook-github-bot
authored andcommitted
fix crash when view with event driven animation is unmounted (facebook#52807)
Summary: Pull Request resolved: facebook#52807 changelog: [internal] Fixes a crash that occurs when event driven animation is attached to a view that is unmounted. Reviewed By: zeyap Differential Revision: D78889689 fbshipit-source-id: 6bc534c3d80a7ed2fc5e0ac378e58343fef45430
1 parent 0d13474 commit dbdf39a

File tree

3 files changed

+101
-37
lines changed

3 files changed

+101
-37
lines changed

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

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,66 @@ test('animation driven by onScroll event', () => {
151151
expect(viewElement.getBoundingClientRect().y).toBe(100);
152152
});
153153

154+
test('animation driven by onScroll event when animated view is unmounted', () => {
155+
const scrollViewRef = createRef<HostInstance>();
156+
157+
component PressableWithNativeDriver(mountAnimatedView: boolean) {
158+
const currScroll = useAnimatedValue(0);
159+
160+
return (
161+
<View style={{flex: 1}}>
162+
{mountAnimatedView ? (
163+
<Animated.View
164+
style={{
165+
position: 'absolute',
166+
width: 10,
167+
height: 10,
168+
transform: [{translateY: currScroll}],
169+
}}
170+
/>
171+
) : null}
172+
<Animated.ScrollView
173+
ref={scrollViewRef}
174+
onScroll={Animated.event(
175+
[
176+
{
177+
nativeEvent: {
178+
contentOffset: {
179+
y: currScroll,
180+
},
181+
},
182+
},
183+
],
184+
{useNativeDriver: true},
185+
)}>
186+
<View style={{height: 1000, width: 100}} />
187+
</Animated.ScrollView>
188+
</View>
189+
);
190+
}
191+
192+
const root = Fantom.createRoot();
193+
Fantom.runTask(() => {
194+
root.render(<PressableWithNativeDriver mountAnimatedView={true} />);
195+
});
196+
197+
Fantom.runTask(() => {
198+
root.render(<PressableWithNativeDriver mountAnimatedView={false} />);
199+
});
200+
201+
const scrollViewelement = ensureInstance(
202+
scrollViewRef.current,
203+
ReactNativeElement,
204+
);
205+
206+
Fantom.scrollTo(scrollViewelement, {
207+
x: 0,
208+
y: 50,
209+
});
210+
211+
Fantom.runWorkLoop();
212+
});
213+
154214
test('animated opacity', () => {
155215
let _opacity;
156216
let _opacityAnimation;

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

Lines changed: 40 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -406,47 +406,51 @@ void NativeAnimatedNodesManager::handleAnimatedEvent(
406406
if (!isOnRenderThread_) {
407407
return;
408408
}
409+
if (eventDrivers_.empty()) {
410+
return;
411+
}
409412

410-
if (!eventDrivers_.empty()) {
411-
bool foundAtLeastOneDriver = false;
412-
413-
const auto key = EventAnimationDriverKey{
414-
.viewTag = viewTag,
415-
.eventName = EventEmitter::normalizeEventType(eventName)};
416-
if (auto driversIter = eventDrivers_.find(key);
417-
driversIter != eventDrivers_.end()) {
418-
auto& drivers = driversIter->second;
419-
if (!drivers.empty()) {
420-
foundAtLeastOneDriver = true;
421-
}
422-
for (const auto& driver : drivers) {
423-
if (auto value = driver->getValueFromPayload(eventPayload)) {
424-
auto node =
425-
getAnimatedNode<ValueAnimatedNode>(driver->getAnimatedNodeTag());
426-
stopAnimationsForNode(node->tag());
427-
if (node->setRawValue(value.value())) {
428-
updatedNodeTags_.insert(node->tag());
429-
}
413+
bool foundAtLeastOneDriver = false;
414+
415+
const auto key = EventAnimationDriverKey{
416+
.viewTag = viewTag,
417+
.eventName = EventEmitter::normalizeEventType(eventName)};
418+
if (auto driversIter = eventDrivers_.find(key);
419+
driversIter != eventDrivers_.end()) {
420+
auto& drivers = driversIter->second;
421+
if (!drivers.empty()) {
422+
foundAtLeastOneDriver = true;
423+
}
424+
for (const auto& driver : drivers) {
425+
if (auto value = driver->getValueFromPayload(eventPayload)) {
426+
auto node =
427+
getAnimatedNode<ValueAnimatedNode>(driver->getAnimatedNodeTag());
428+
if (node == nullptr) {
429+
continue;
430+
}
431+
stopAnimationsForNode(node->tag());
432+
if (node->setRawValue(value.value())) {
433+
updatedNodeTags_.insert(node->tag());
430434
}
431435
}
432436
}
437+
}
433438

434-
if (foundAtLeastOneDriver && !isEventAnimationInProgress_) {
435-
// There is an animation driver handling this event and
436-
// event driven animation has not been started yet.
437-
isEventAnimationInProgress_ = true;
438-
// Some platforms (e.g. iOS) have UI tick listener disable
439-
// when there are no active animations. Calling
440-
// `startRenderCallbackIfNeeded` will call platform specific code to
441-
// register UI tick listener.
442-
startRenderCallbackIfNeeded();
443-
// Calling startOnRenderCallback_ will register a UI tick listener.
444-
// The UI ticker listener will not be called until the next frame.
445-
// That's why, in case this is called from the UI thread, we need to
446-
// proactivelly trigger the animation loop to avoid showing stale
447-
// frames.
448-
onRender();
449-
}
439+
if (foundAtLeastOneDriver && !isEventAnimationInProgress_) {
440+
// There is an animation driver handling this event and
441+
// event driven animation has not been started yet.
442+
isEventAnimationInProgress_ = true;
443+
// Some platforms (e.g. iOS) have UI tick listener disable
444+
// when there are no active animations. Calling
445+
// `startRenderCallbackIfNeeded` will call platform specific code to
446+
// register UI tick listener.
447+
startRenderCallbackIfNeeded();
448+
// Calling startOnRenderCallback_ will register a UI tick listener.
449+
// The UI ticker listener will not be called until the next frame.
450+
// That's why, in case this is called from the UI thread, we need to
451+
// proactivelly trigger the animation loop to avoid showing stale
452+
// frames.
453+
onRender();
450454
}
451455
}
452456

packages/react-native/ReactCxxPlatform/react/renderer/animated/event_drivers/EventAnimationDriver.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class EventAnimationDriver {
3737
protected:
3838
std::vector<std::string> eventPath_;
3939

40-
Tag animatedValueTag_;
40+
const Tag animatedValueTag_;
4141
};
4242

4343
struct EventAnimationDriverKey {

0 commit comments

Comments
 (0)