Skip to content

Commit 6ad1299

Browse files
authored
refactor: Fix exhaustive deps linting errors (#3605)
1 parent f308cd6 commit 6ad1299

File tree

6 files changed

+760
-86
lines changed

6 files changed

+760
-86
lines changed

src/components/Banner.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import * as React from 'react';
22
import { Animated, StyleProp, StyleSheet, View, ViewStyle } from 'react-native';
33

4+
import useEventCallback from 'use-event-callback';
5+
46
import { useInternalTheme } from '../core/theming';
57
import type { $RemoveChildren, ThemeProp } from '../types';
68
import Button from './Button/Button';
@@ -149,6 +151,9 @@ const Banner = ({
149151
measured: false,
150152
});
151153

154+
const showCallback = useEventCallback(onShowAnimationFinished);
155+
const hideCallback = useEventCallback(onHideAnimationFinished);
156+
152157
const { scale } = theme.animation;
153158

154159
React.useEffect(() => {
@@ -158,14 +163,14 @@ const Banner = ({
158163
duration: 250 * scale,
159164
toValue: 1,
160165
useNativeDriver: false,
161-
}).start(onShowAnimationFinished);
166+
}).start(showCallback);
162167
} else {
163168
// hide
164169
Animated.timing(position, {
165170
duration: 200 * scale,
166171
toValue: 0,
167172
useNativeDriver: false,
168-
}).start(onHideAnimationFinished);
173+
}).start(hideCallback);
169174
}
170175
// eslint-disable-next-line react-hooks/exhaustive-deps
171176
}, [visible, position, scale]);

src/components/Modal.tsx

Lines changed: 32 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {
33
Animated,
44
BackHandler,
55
Easing,
6-
NativeEventSubscription,
76
StyleProp,
87
StyleSheet,
98
TouchableWithoutFeedback,
@@ -12,6 +11,7 @@ import {
1211
} from 'react-native';
1312

1413
import { useSafeAreaInsets } from 'react-native-safe-area-context';
14+
import useEventCallback from 'use-event-callback';
1515
import type { ThemeProp } from 'src/types';
1616

1717
import { useInternalTheme } from '../core/theming';
@@ -104,7 +104,7 @@ function Modal({
104104
dismissable = true,
105105
visible = false,
106106
overlayAccessibilityLabel = 'Close modal',
107-
onDismiss,
107+
onDismiss = () => {},
108108
children,
109109
contentContainerStyle,
110110
style,
@@ -118,6 +118,8 @@ function Modal({
118118
visibleRef.current = visible;
119119
});
120120

121+
const onDismissCallback = useEventCallback(onDismiss);
122+
121123
const { scale } = theme.animation;
122124

123125
const { top, bottom } = useSafeAreaInsets();
@@ -130,44 +132,16 @@ function Modal({
130132
setRendered(true);
131133
}
132134

133-
const handleBack = () => {
134-
if (dismissable) {
135-
hideModal();
136-
}
137-
return true;
138-
};
139-
140-
const subscription = React.useRef<NativeEventSubscription | undefined>(
141-
undefined
142-
);
143-
144-
const showModal = () => {
145-
subscription.current?.remove();
146-
subscription.current = addEventListener(
147-
BackHandler,
148-
'hardwareBackPress',
149-
handleBack
150-
);
151-
135+
const showModal = React.useCallback(() => {
152136
Animated.timing(opacity, {
153137
toValue: 1,
154138
duration: scale * DEFAULT_DURATION,
155139
easing: Easing.out(Easing.cubic),
156140
useNativeDriver: true,
157141
}).start();
158-
};
159-
160-
const removeListeners = () => {
161-
if (subscription.current?.remove) {
162-
subscription.current?.remove();
163-
} else {
164-
BackHandler.removeEventListener('hardwareBackPress', handleBack);
165-
}
166-
};
167-
168-
const hideModal = () => {
169-
removeListeners();
142+
}, [opacity, scale]);
170143

144+
const hideModal = React.useCallback(() => {
171145
Animated.timing(opacity, {
172146
toValue: 0,
173147
duration: scale * DEFAULT_DURATION,
@@ -178,8 +152,8 @@ function Modal({
178152
return;
179153
}
180154

181-
if (visible && onDismiss) {
182-
onDismiss();
155+
if (visible) {
156+
onDismissCallback();
183157
}
184158

185159
if (visibleRef.current) {
@@ -188,7 +162,28 @@ function Modal({
188162
setRendered(false);
189163
}
190164
});
191-
};
165+
}, [onDismissCallback, opacity, scale, showModal, visible]);
166+
167+
React.useEffect(() => {
168+
if (!visible) {
169+
return undefined;
170+
}
171+
172+
const onHardwareBackPress = () => {
173+
if (dismissable) {
174+
hideModal();
175+
}
176+
177+
return true;
178+
};
179+
180+
const subscription = addEventListener(
181+
BackHandler,
182+
'hardwareBackPress',
183+
onHardwareBackPress
184+
);
185+
return () => subscription.remove();
186+
}, [dismissable, hideModal, visible]);
192187

193188
const prevVisible = React.useRef<boolean | null>(null);
194189

@@ -203,11 +198,6 @@ function Modal({
203198
prevVisible.current = visible;
204199
});
205200

206-
React.useEffect(() => {
207-
return removeListeners;
208-
// eslint-disable-next-line react-hooks/exhaustive-deps
209-
}, []);
210-
211201
if (!rendered) return null;
212202

213203
return (
@@ -247,6 +237,7 @@ function Modal({
247237
testID={`${testID}-wrapper`}
248238
>
249239
<Surface
240+
testID={`${testID}-surface`}
250241
theme={theme}
251242
style={
252243
[

src/components/ProgressBar.tsx

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ const ProgressBar = ({
8383
new Animated.Value(0)
8484
);
8585
const { current: fade } = React.useRef<Animated.Value>(new Animated.Value(0));
86+
const passedAnimatedValue =
87+
React.useRef<Props['animatedValue']>(animatedValue);
8688
const [width, setWidth] = React.useState<number>(0);
8789
const [prevWidth, setPrevWidth] = React.useState<number>(0);
8890

@@ -91,6 +93,10 @@ const ProgressBar = ({
9193

9294
const { scale } = theme.animation;
9395

96+
React.useEffect(() => {
97+
passedAnimatedValue.current = animatedValue;
98+
});
99+
94100
const startAnimation = React.useCallback(() => {
95101
// Show progress bar
96102
Animated.timing(fade, {
@@ -100,7 +106,17 @@ const ProgressBar = ({
100106
isInteraction: false,
101107
}).start();
102108

103-
if (animatedValue && animatedValue >= 0) {
109+
/**
110+
* We shouldn't add @param animatedValue to the
111+
* deps array, to avoid the unnecessary loop.
112+
* We can only check if the prop is passed initially,
113+
* and we do early return.
114+
*/
115+
const externalAnimation =
116+
typeof passedAnimatedValue.current !== 'undefined' &&
117+
passedAnimatedValue.current >= 0;
118+
119+
if (externalAnimation) {
104120
return;
105121
}
106122

@@ -128,13 +144,6 @@ const ProgressBar = ({
128144
isInteraction: false,
129145
}).start();
130146
}
131-
/**
132-
* We shouldn't add @param animatedValue to the
133-
* deps array, to avoid the unnecessary loop.
134-
* We can only check if the prop is passed initially,
135-
* and we do early return.
136-
*/
137-
// eslint-disable-next-line react-hooks/exhaustive-deps
138147
}, [fade, scale, indeterminate, timer, progress]);
139148

140149
const stopAnimation = React.useCallback(() => {

0 commit comments

Comments
 (0)