Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 24 additions & 14 deletions src/components/Lottie/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {NavigationContainerRefContext, NavigationContext} from '@react-navigatio
import type {AnimationObject, LottieViewProps} from 'lottie-react-native';
import LottieView from 'lottie-react-native';
import type {ForwardedRef} from 'react';
import React, {forwardRef, useContext, useEffect, useState} from 'react';
import React, {forwardRef, useContext, useEffect, useRef, useState} from 'react';
import {InteractionManager, View} from 'react-native';
import type DotLottieAnimation from '@components/LottieAnimations/types';
import useAppState from '@hooks/useAppState';
Expand All @@ -18,7 +18,8 @@ type Props = {
shouldLoadAfterInteractions?: boolean;
} & Omit<LottieViewProps, 'source'>;

function Lottie({source, webStyle, shouldLoadAfterInteractions, ...props}: Props, ref: ForwardedRef<LottieView>) {
function Lottie({source, webStyle, shouldLoadAfterInteractions, ...props}: Props, forwardedRef: ForwardedRef<LottieView>) {
const animationRef = useRef<LottieView | null>(null);
const appState = useAppState();
const {splashScreenState} = useSplashScreenStateContext();
const styles = useThemeStyles();
Expand Down Expand Up @@ -79,19 +80,20 @@ function Lottie({source, webStyle, shouldLoadAfterInteractions, ...props}: Props
return unsubscribeNavigationBlur;
}, [browser, navigationContainerRef, navigator]);

// If user is being navigated away, let pause the animation to prevent memory leak.
Comment thread
MonilBhavsar marked this conversation as resolved.
// see issue: https://github.com/Expensify/App/issues/36645
useEffect(() => {
if (!animationRef.current || !hasNavigatedAway) {
return;
}
animationRef?.current?.pause();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the browser back button is used to navigate back to a page containing lottie animation, the paused animation does not animate again as a rerender was needed. We addressed this issue here at #59325

}, [hasNavigatedAway]);

// If the page navigates to another screen, the image fails to load, app is in background state, animation file isn't ready, or the splash screen isn't hidden yet,
// we'll just render an empty view as the fallback to prevent
// 1. memory leak, see issue: https://github.com/Expensify/App/issues/36645
// 2. heavy rendering, see issues: https://github.com/Expensify/App/issues/34696 and https://github.com/Expensify/App/issues/47273
// 3. lag on react navigation transitions, see issue: https://github.com/Expensify/App/issues/44812
if (
hasNavigatedAway ||
isError ||
appState.isBackground ||
!animationFile ||
splashScreenState !== CONST.BOOT_SPLASH_STATE.HIDDEN ||
(!isInteractionComplete && shouldLoadAfterInteractions)
) {
// 1. heavy rendering, see issues: https://github.com/Expensify/App/issues/34696 and https://github.com/Expensify/App/issues/47273
// 2. lag on react navigation transitions, see issue: https://github.com/Expensify/App/issues/44812
if (isError || appState.isBackground || !animationFile || splashScreenState !== CONST.BOOT_SPLASH_STATE.HIDDEN || (!isInteractionComplete && shouldLoadAfterInteractions)) {
return <View style={[aspectRatioStyle, props.style]} />;
}

Expand All @@ -100,7 +102,15 @@ function Lottie({source, webStyle, shouldLoadAfterInteractions, ...props}: Props
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
source={animationFile}
ref={ref}
ref={(ref) => {
if (typeof forwardedRef === 'function') {
forwardedRef(ref);
} else if (forwardedRef && 'current' in forwardedRef) {
// eslint-disable-next-line no-param-reassign
forwardedRef.current = ref;
}
animationRef.current = ref;
}}
style={[aspectRatioStyle, props.style]}
webStyle={{...aspectRatioStyle, ...webStyle}}
onAnimationFailure={() => setIsError(true)}
Expand Down