Skip to content

Commit

Permalink
feat: stop measures and reactions on unmount
Browse files Browse the repository at this point in the history
  • Loading branch information
johnhaup committed Jan 28, 2025
1 parent f705801 commit d9e9212
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 49 deletions.
55 changes: 23 additions & 32 deletions src/components/LazyChild/components/FullLazyChild.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useEffect } from 'react';
import React, { useCallback } from 'react';
import { Dimensions, type LayoutChangeEvent } from 'react-native';
import Animated, {
measure,
Expand All @@ -10,10 +10,10 @@ import Animated, {
useSharedValue,
} from 'react-native-reanimated';
import { useAnimatedContext } from '../../../context/AnimatedContext';
import { logger } from '../../../utils/logger';
import { useEnteringCallbacks } from '../hooks/useEnteringCallbacks';
import { useVisibilityCallbacks } from '../hooks/useVisibilityCallbacks';
import { LazyChildProps } from '../types';
import { logger } from '../../../utils/logger';

const SCREEN_HEIGHT = Dimensions.get('window').height;

Expand All @@ -37,18 +37,9 @@ export function FullLazyChild({
containerStart,
containerEnd,
horizontal,
isScrollUnmounted,
} = useAnimatedContext();

const _isMounted = useSharedValue(false);

useEffect(() => {
_isMounted.value = true;

return () => {
_isMounted.value = false;
};
}, [_isMounted]);

/**
* If onLayout returns a height or width value greater than 0.
*/
Expand All @@ -64,25 +55,25 @@ export function FullLazyChild({

const _debug = useSharedValue(debug);

const _shouldFireThresholdEnter = useSharedValue(
typeof onEnterThresholdPass === 'function'
);
const _shouldFireThresholdExit = useSharedValue(
typeof onExitThresholdPass === 'function'
);
const _shouldMeasurePercentVisible = useSharedValue(
typeof onVisibilityEnter === 'function'
);
const _shouldFireVisibilityExit = useSharedValue(
typeof onVisibilityExit === 'function'
);
const shouldFireThresholdEnter = useDerivedValue(() => {
return typeof onEnterThresholdPass === 'function' && !isScrollUnmounted;
});
const shouldFireThresholdExit = useDerivedValue(() => {
return typeof onExitThresholdPass === 'function' && !isScrollUnmounted;
});
const shouldMeasurePercentVisible = useDerivedValue(() => {
return typeof onVisibilityEnter === 'function' && !isScrollUnmounted;
});
const shouldFireVisibilityExit = useDerivedValue(() => {
return typeof onVisibilityExit === 'function' && !isScrollUnmounted;
});

const _hasValidCallback = useDerivedValue(
() =>
_shouldFireThresholdEnter.value ||
_shouldFireThresholdExit.value ||
_shouldMeasurePercentVisible.value ||
_shouldFireVisibilityExit.value
shouldFireThresholdEnter.value ||
shouldFireThresholdExit.value ||
shouldMeasurePercentVisible.value ||
shouldFireVisibilityExit.value
);

function measureView() {
Expand Down Expand Up @@ -123,8 +114,8 @@ export function FullLazyChild({
onEnterThresholdPass,
onExitThresholdPass,
_measurement,
_shouldFireThresholdEnter,
_shouldFireThresholdExit,
shouldFireThresholdEnter,
shouldFireThresholdExit,
startTrigger,
endTrigger,
horizontal,
Expand All @@ -134,8 +125,8 @@ export function FullLazyChild({
percentVisibleThreshold,
onVisibilityEnter,
onVisibilityExit,
_shouldMeasurePercentVisible,
_shouldFireVisibilityExit,
shouldMeasurePercentVisible,
shouldFireVisibilityExit,
_measurement,
containerStart,
containerEnd,
Expand Down
16 changes: 8 additions & 8 deletions src/components/LazyChild/hooks/useEnteringCallbacks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ export const useEnteringCallbacks = ({
onEnterThresholdPass,
onExitThresholdPass,
_measurement,
_shouldFireThresholdEnter,
_shouldFireThresholdExit,
shouldFireThresholdEnter,
shouldFireThresholdExit,
startTrigger,
endTrigger,
horizontal,
}: {
onEnterThresholdPass?: () => void;
onExitThresholdPass?: () => void;
_measurement: SharedValue<ReturnType<typeof measure>>;
_shouldFireThresholdEnter: SharedValue<boolean>;
_shouldFireThresholdExit: SharedValue<boolean>;
shouldFireThresholdEnter: SharedValue<boolean>;
shouldFireThresholdExit: SharedValue<boolean>;
startTrigger: Pick<SharedValue<number>, 'value'>;
endTrigger: Pick<SharedValue<number>, 'value'>;
horizontal: Pick<SharedValue<boolean>, 'value'>;
Expand All @@ -35,9 +35,9 @@ export const useEnteringCallbacks = ({
_hasFiredThresholdEntered.value = true;
_hasFiredThresholdExited.value = false;

if (!_shouldFireThresholdExit.value) {
if (!shouldFireThresholdExit.value) {
// Enter callback has fired and there is no exit callback, so it cannot refire. Set shouldFire to false to prevent unnecessary measures.
_shouldFireThresholdEnter.value = false;
shouldFireThresholdEnter.value = false;
}

onEnterThresholdPass();
Expand Down Expand Up @@ -78,11 +78,11 @@ export const useEnteringCallbacks = ({
() => _hasEntered.value,
(hasLazyChildEntered) => {
if (hasLazyChildEntered) {
if (_shouldFireThresholdEnter.value) {
if (shouldFireThresholdEnter.value) {
runOnJS(handleThresholdEntered)();
}
} else {
if (_shouldFireThresholdExit.value) {
if (shouldFireThresholdExit.value) {
runOnJS(handleOnThresholdExited)();
}
}
Expand Down
16 changes: 8 additions & 8 deletions src/components/LazyChild/hooks/useVisibilityCallbacks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ interface Props {
percentVisibleThreshold: number;
onVisibilityEnter?: () => void;
onVisibilityExit?: () => void;
_shouldMeasurePercentVisible: SharedValue<boolean>;
_shouldFireVisibilityExit: SharedValue<boolean>;
shouldMeasurePercentVisible: SharedValue<boolean>;
shouldFireVisibilityExit: SharedValue<boolean>;
_measurement: SharedValue<ReturnType<typeof measure>>;
containerStart: Pick<SharedValue<number>, 'value'>;
containerEnd: Pick<SharedValue<number>, 'value'>;
Expand All @@ -24,8 +24,8 @@ export const useVisibilityCallbacks = ({
percentVisibleThreshold,
onVisibilityEnter,
onVisibilityExit,
_shouldMeasurePercentVisible,
_shouldFireVisibilityExit,
shouldMeasurePercentVisible,
shouldFireVisibilityExit,
_measurement,
containerStart,
containerEnd,
Expand All @@ -41,9 +41,9 @@ export const useVisibilityCallbacks = ({
_hasFiredOnVisibilityEntered.value = true;
_hasFiredOnVisibilityExited.value = false;

if (!_shouldFireVisibilityExit.value) {
if (!shouldFireVisibilityExit.value) {
// Enter callback has fired and there is no exit callback, so it cannot refire. Set shouldFire to false to prevent unnecessary measures.
_shouldMeasurePercentVisible.value = false;
shouldMeasurePercentVisible.value = false;
}

onVisibilityEnter();
Expand Down Expand Up @@ -96,11 +96,11 @@ export const useVisibilityCallbacks = ({
() => _isVisible.value,
(isLazyChildVisible) => {
if (isLazyChildVisible) {
if (_shouldMeasurePercentVisible.value) {
if (shouldMeasurePercentVisible.value) {
runOnJS(handleOnVisibilityEntered)();
}
} else {
if (_shouldFireVisibilityExit.value) {
if (shouldFireVisibilityExit.value) {
runOnJS(handleOnVisibilityExited)();
}
}
Expand Down
11 changes: 10 additions & 1 deletion src/components/useLazyContextValues.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useMemo } from 'react';
import React, { useCallback, useLayoutEffect, useMemo } from 'react';
import { StatusBar, type LayoutChangeEvent } from 'react-native';
import Animated, {
AnimatedRef,
Expand Down Expand Up @@ -42,6 +42,14 @@ export const useLazyContextValues = ({
*/
const scrollValue = useScrollViewOffset(ref);

const isScrollUnmounted = useSharedValue(false);
useLayoutEffect(() => {
return () => {
isScrollUnmounted.value = true;
};
// eslint-disable-next-line react-hooks/exhaustive-deps -- shared values do not trigger re-renders
}, []);

const containerStart = useDerivedValue(() =>
_horizontal.value
? _containerCoordinates.value.x
Expand Down Expand Up @@ -107,6 +115,7 @@ export const useLazyContextValues = ({
startTrigger,
endTrigger,
horizontal: _horizontal,
isScrollUnmounted,
}),
// eslint-disable-next-line react-hooks/exhaustive-deps -- shared values do not trigger re-renders
[]
Expand Down
1 change: 1 addition & 0 deletions src/context/AnimatedContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const initialContext = {
startTrigger: { value: 0 },
endTrigger: { value: 0 },
horizontal: { value: false },
isScrollUnmounted: { value: false },
};

export const AnimatedContext = createContext(initialContext);
Expand Down

0 comments on commit d9e9212

Please sign in to comment.