Skip to content

Commit 19774a6

Browse files
committed
perf: critical str performance issue messagelist scrolling
1 parent fd28421 commit 19774a6

File tree

1 file changed

+112
-70
lines changed

1 file changed

+112
-70
lines changed

package/src/components/Message/MessageSimple/MessageSimple.tsx

Lines changed: 112 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useMemo, useState } from 'react';
1+
import React, { useCallback, useMemo, useState } from 'react';
22
import { Dimensions, LayoutChangeEvent, StyleSheet, View } from 'react-native';
33

44
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
@@ -12,6 +12,8 @@ import Animated, {
1212
withSpring,
1313
} from 'react-native-reanimated';
1414

15+
const AnimatedWrapper = Animated.createAnimatedComponent(View);
16+
1517
import {
1618
MessageContextValue,
1719
useMessageContext,
@@ -205,77 +207,104 @@ const MessageSimpleWithContext = <
205207

206208
const translateX = useSharedValue(0);
207209
const touchStart = useSharedValue<{ x: number; y: number } | null>(null);
210+
const isSwiping = useSharedValue<boolean>(false);
211+
const [isBeingSwiped, setIsBeingSwiped] = useState<boolean>(false);
208212

209-
const onSwipeToReply = () => {
213+
const onSwipeToReply = useCallback(() => {
210214
clearQuotedMessageState();
211215
setQuotedMessageState(message);
212-
};
216+
}, [clearQuotedMessageState, message, setQuotedMessageState]);
213217

214218
const THRESHOLD = 25;
215219

216220
const triggerHaptic = NativeHandlers.triggerHaptic;
217221

218-
const swipeGesture = Gesture.Pan()
219-
.hitSlop(messageSwipeToReplyHitSlop)
220-
.onBegin((event) => {
221-
touchStart.value = { x: event.x, y: event.y };
222-
})
223-
.onTouchesMove((event, state) => {
224-
if (!touchStart.value || !event.changedTouches.length) {
225-
state.fail();
226-
return;
227-
}
228-
229-
const xDiff = Math.abs(event.changedTouches[0].x - touchStart.value.x);
230-
const yDiff = Math.abs(event.changedTouches[0].y - touchStart.value.y);
231-
const isHorizontalPanning = xDiff > yDiff;
232-
233-
if (isHorizontalPanning) {
234-
state.activate();
235-
} else {
236-
state.fail();
237-
}
238-
})
239-
.onStart(() => {
240-
translateX.value = 0;
241-
})
242-
.onChange(({ translationX }) => {
243-
if (translationX > 0) {
244-
translateX.value = translationX;
245-
}
246-
})
247-
.onEnd(() => {
248-
if (translateX.value >= THRESHOLD) {
249-
runOnJS(onSwipeToReply)();
250-
if (triggerHaptic) {
251-
runOnJS(triggerHaptic)('impactMedium');
252-
}
253-
}
254-
translateX.value = withSpring(0, {
255-
dampingRatio: 1,
256-
duration: 500,
257-
overshootClamping: true,
258-
stiffness: 1,
259-
});
260-
});
261-
262-
const messageBubbleAnimatedStyle = useAnimatedStyle(() => ({
263-
transform: [{ translateX: translateX.value }],
264-
}));
265-
266-
const swipeContentAnimatedStyle = useAnimatedStyle(() => ({
267-
opacity: interpolate(translateX.value, [0, THRESHOLD], [0, 1]),
268-
transform: [
269-
{
270-
translateX: interpolate(
271-
translateX.value,
272-
[0, THRESHOLD],
273-
[-THRESHOLD, 0],
274-
Extrapolation.CLAMP,
275-
),
276-
},
277-
],
278-
}));
222+
const swipeGesture = useMemo(
223+
() =>
224+
Gesture.Pan()
225+
.hitSlop(messageSwipeToReplyHitSlop)
226+
.onBegin((event) => {
227+
touchStart.value = { x: event.x, y: event.y };
228+
})
229+
.onTouchesMove((event, state) => {
230+
if (!touchStart.value || !event.changedTouches.length) {
231+
state.fail();
232+
return;
233+
}
234+
235+
const xDiff = Math.abs(event.changedTouches[0].x - touchStart.value.x);
236+
const yDiff = Math.abs(event.changedTouches[0].y - touchStart.value.y);
237+
const isHorizontalPanning = xDiff > yDiff;
238+
239+
if (isHorizontalPanning) {
240+
state.activate();
241+
isSwiping.value = true;
242+
runOnJS(setIsBeingSwiped)(true);
243+
} else {
244+
state.fail();
245+
}
246+
})
247+
.onStart(() => {
248+
translateX.value = 0;
249+
})
250+
.onChange(({ translationX }) => {
251+
if (translationX > 0) {
252+
translateX.value = translationX;
253+
}
254+
})
255+
.onEnd(() => {
256+
if (translateX.value >= THRESHOLD) {
257+
runOnJS(onSwipeToReply)();
258+
if (triggerHaptic) {
259+
runOnJS(triggerHaptic)('impactMedium');
260+
}
261+
}
262+
translateX.value = withSpring(
263+
0,
264+
{
265+
dampingRatio: 1,
266+
duration: 500,
267+
overshootClamping: true,
268+
stiffness: 1,
269+
},
270+
() => {
271+
isSwiping.value = false;
272+
runOnJS(setIsBeingSwiped)(false);
273+
},
274+
);
275+
}),
276+
[isSwiping, messageSwipeToReplyHitSlop, onSwipeToReply, touchStart, translateX, triggerHaptic],
277+
);
278+
279+
const messageBubbleAnimatedStyle = useAnimatedStyle(
280+
() =>
281+
isSwiping.value
282+
? {
283+
transform: [{ translateX: translateX.value }],
284+
}
285+
: {},
286+
[],
287+
);
288+
289+
const swipeContentAnimatedStyle = useAnimatedStyle(
290+
() =>
291+
isSwiping.value
292+
? {
293+
opacity: interpolate(translateX.value, [0, THRESHOLD], [0, 1]),
294+
transform: [
295+
{
296+
translateX: interpolate(
297+
translateX.value,
298+
[0, THRESHOLD],
299+
[-THRESHOLD, 0],
300+
Extrapolation.CLAMP,
301+
),
302+
},
303+
],
304+
}
305+
: {},
306+
[],
307+
);
279308

280309
const renderMessageBubble = useMemo(
281310
() => (
@@ -309,18 +338,31 @@ const MessageSimpleWithContext = <
309338
() => (
310339
<GestureDetector gesture={swipeGesture}>
311340
<View hitSlop={messageSwipeToReplyHitSlop} style={[styles.contentWrapper, contentWrapper]}>
312-
<Animated.View
313-
style={[styles.swipeContentContainer, swipeContentAnimatedStyle, swipeContentContainer]}
314-
>
315-
{MessageSwipeContent ? <MessageSwipeContent /> : null}
316-
</Animated.View>
317-
<Animated.View style={messageBubbleAnimatedStyle}>{renderMessageBubble}</Animated.View>
341+
{isBeingSwiped ? (
342+
<>
343+
<AnimatedWrapper
344+
style={[
345+
styles.swipeContentContainer,
346+
swipeContentAnimatedStyle,
347+
swipeContentContainer,
348+
]}
349+
>
350+
{MessageSwipeContent ? <MessageSwipeContent /> : null}
351+
</AnimatedWrapper>
352+
<AnimatedWrapper pointerEvents='box-none' style={messageBubbleAnimatedStyle}>
353+
{renderMessageBubble}
354+
</AnimatedWrapper>
355+
</>
356+
) : (
357+
renderMessageBubble
358+
)}
318359
</View>
319360
</GestureDetector>
320361
),
321362
[
322363
MessageSwipeContent,
323364
contentWrapper,
365+
isBeingSwiped,
324366
messageBubbleAnimatedStyle,
325367
messageSwipeToReplyHitSlop,
326368
renderMessageBubble,

0 commit comments

Comments
 (0)