Skip to content

Commit 7de8615

Browse files
authored
Merge pull request #2148 from didi/fix-runonjs-memory
feat: rn 环境 swiper&movable&scroll-view 规避 runOnJs 内存泄漏问题
2 parents 490294b + c4f9b61 commit 7de8615

File tree

3 files changed

+64
-49
lines changed

3 files changed

+64
-49
lines changed

packages/webpack-plugin/lib/runtime/components/react/mpx-movable-view.tsx

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,14 @@ import { StyleSheet, View, LayoutChangeEvent } from 'react-native'
2222
import useInnerProps, { getCustomEvent } from './getInnerListeners'
2323
import useNodesRef, { HandlerRef } from './useNodesRef'
2424
import { MovableAreaContext } from './context'
25-
import { useTransformStyle, splitProps, splitStyle, HIDDEN_STYLE, wrapChildren, GestureHandler, flatGesture, extendObject, omit, useNavigation } from './utils'
25+
import { useTransformStyle, splitProps, splitStyle, HIDDEN_STYLE, wrapChildren, GestureHandler, flatGesture, extendObject, omit, useNavigation, useRunOnJSCallback } from './utils'
2626
import { GestureDetector, Gesture, GestureTouchEvent, GestureStateChangeEvent, PanGestureHandlerEventPayload, PanGesture } from 'react-native-gesture-handler'
2727
import Animated, {
2828
useSharedValue,
2929
useAnimatedStyle,
3030
withDecay,
3131
runOnJS,
3232
runOnUI,
33-
useAnimatedReaction,
3433
withSpring
3534
} from 'react-native-reanimated'
3635
import { collectDataset, noop } from '@mpxjs/utils'
@@ -87,7 +86,6 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
8786
const layoutRef = useRef<any>({})
8887
const changeSource = useRef<any>('')
8988
const hasLayoutRef = useRef(false)
90-
9189
const propsRef = useRef<any>({})
9290
propsRef.current = (props || {}) as MovableViewProps
9391

@@ -208,7 +206,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
208206
const now = Date.now()
209207
if (now - lastChangeTime.value >= changeThrottleTime) {
210208
lastChangeTime.value = now
211-
runOnJS(handleTriggerChange)({ x, y, type })
209+
runOnJS(runOnJSCallback)('handleTriggerChange', { x, y, type })
212210
}
213211
}, [changeThrottleTime])
214212

@@ -233,7 +231,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
233231
: newY
234232
}
235233
if (bindchange) {
236-
runOnJS(handleTriggerChange)({
234+
runOnJS(runOnJSCallback)('handleTriggerChange', {
237235
x: newX,
238236
y: newY,
239237
type: 'setData'
@@ -408,13 +406,21 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
408406
catchtouchend && catchtouchend(e)
409407
}
410408

409+
const runOnJSCallbackRef = useRef({
410+
handleTriggerChange,
411+
triggerStartOnJS,
412+
triggerMoveOnJS,
413+
triggerEndOnJS
414+
})
415+
const runOnJSCallback = useRunOnJSCallback(runOnJSCallbackRef)
416+
411417
const gesture = useMemo(() => {
412418
const handleTriggerMove = (e: GestureTouchEvent) => {
413419
'worklet'
414420
const hasTouchmove = !!bindhtouchmove || !!bindvtouchmove || !!bindtouchmove
415421
const hasCatchTouchmove = !!catchhtouchmove || !!catchvtouchmove || !!catchtouchmove
416422
if (hasTouchmove || hasCatchTouchmove) {
417-
runOnJS(triggerMoveOnJS)({
423+
runOnJS(runOnJSCallback)('triggerMoveOnJS', {
418424
e,
419425
touchEvent: touchEvent.value,
420426
hasTouchmove,
@@ -433,7 +439,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
433439
y: changedTouches.y
434440
}
435441
if (bindtouchstart || catchtouchstart) {
436-
runOnJS(triggerStartOnJS)({ e })
442+
runOnJS(runOnJSCallback)('triggerStartOnJS', { e })
437443
}
438444
})
439445
.onStart(() => {
@@ -487,7 +493,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
487493
isFirstTouch.value = true
488494
isMoving.value = false
489495
if (bindtouchend || catchtouchend) {
490-
runOnJS(triggerEndOnJS)({ e })
496+
runOnJS(runOnJSCallback)('triggerEndOnJS', { e })
491497
}
492498
})
493499
.onEnd((e: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
@@ -515,7 +521,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
515521
: y
516522
}
517523
if (bindchange) {
518-
runOnJS(handleTriggerChange)({
524+
runOnJS(runOnJSCallback)('handleTriggerChange', {
519525
x,
520526
y
521527
})
@@ -532,7 +538,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
532538
}, () => {
533539
xInertialMotion.value = false
534540
if (bindchange) {
535-
runOnJS(handleTriggerChange)({
541+
runOnJS(runOnJSCallback)('handleTriggerChange', {
536542
x: offsetX.value,
537543
y: offsetY.value
538544
})
@@ -548,7 +554,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
548554
}, () => {
549555
yInertialMotion.value = false
550556
if (bindchange) {
551-
runOnJS(handleTriggerChange)({
557+
runOnJS(runOnJSCallback)('handleTriggerChange', {
552558
x: offsetX.value,
553559
y: offsetY.value
554560
})

packages/webpack-plugin/lib/runtime/components/react/mpx-scroll-view.tsx

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import Animated, { useSharedValue, withTiming, useAnimatedStyle, runOnJS } from
3838
import { warn, hasOwn } from '@mpxjs/utils'
3939
import useInnerProps, { getCustomEvent } from './getInnerListeners'
4040
import useNodesRef, { HandlerRef } from './useNodesRef'
41-
import { splitProps, splitStyle, useTransformStyle, useLayout, wrapChildren, extendObject, flatGesture, GestureHandler, HIDDEN_STYLE } from './utils'
41+
import { splitProps, splitStyle, useTransformStyle, useLayout, wrapChildren, extendObject, flatGesture, GestureHandler, HIDDEN_STYLE, useRunOnJSCallback } from './utils'
4242
import { IntersectionObserverContext, ScrollViewContext } from './context'
4343
import Portal from './mpx-portal'
4444

@@ -210,6 +210,15 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
210210
const { textStyle, innerStyle = {} } = splitStyle(normalStyle)
211211

212212
const scrollViewRef = useRef<ScrollView>(null)
213+
214+
const runOnJSCallbackRef = useRef({
215+
setEnableScroll,
216+
setScrollBounces,
217+
setRefreshing,
218+
onRefresh
219+
})
220+
const runOnJSCallback = useRunOnJSCallback(runOnJSCallbackRef)
221+
213222
useNodesRef(props, ref, scrollViewRef, {
214223
style: normalStyle,
215224
scrollOffset: scrollOptions,
@@ -587,7 +596,7 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
587596
'worklet'
588597
if (enableScrollValue.value !== newValue) {
589598
enableScrollValue.value = newValue
590-
runOnJS(setEnableScroll)(newValue)
599+
runOnJS(runOnJSCallback)('setEnableScroll', newValue)
591600
}
592601
}
593602

@@ -600,7 +609,7 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
600609
'worklet'
601610
if (bouncesValue.value !== newValue) {
602611
bouncesValue.value = newValue
603-
runOnJS(setScrollBounces)(newValue)
612+
runOnJS(runOnJSCallback)('setScrollBounces', newValue)
604613
}
605614
}
606615

@@ -649,19 +658,19 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
649658
if ((event.translationY > 0 && translateY.value < refresherThreshold) || event.translationY < 0) {
650659
translateY.value = withTiming(0)
651660
updateScrollState(true)
652-
runOnJS(setRefreshing)(false)
661+
runOnJS(runOnJSCallback)('setRefreshing', false)
653662
} else {
654663
translateY.value = withTiming(refresherHeight.value)
655664
}
656665
} else if (event.translationY >= refresherHeight.value) {
657666
// 触发刷新
658667
translateY.value = withTiming(refresherHeight.value)
659-
runOnJS(onRefresh)()
668+
runOnJS(runOnJSCallback)('onRefresh')
660669
} else {
661670
// 回弹
662671
translateY.value = withTiming(0)
663672
updateScrollState(true)
664-
runOnJS(setRefreshing)(false)
673+
runOnJS(runOnJSCallback)('setRefreshing', false)
665674
}
666675
})
667676
.simultaneousWithExternalGesture(scrollViewRef)

packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing, runOnJS
55
import React, { JSX, forwardRef, useRef, useEffect, ReactNode, ReactElement, useMemo, createElement } from 'react'
66
import useInnerProps, { getCustomEvent } from './getInnerListeners'
77
import useNodesRef, { HandlerRef } from './useNodesRef' // 引入辅助函数
8-
import { useTransformStyle, splitStyle, splitProps, useLayout, wrapChildren, extendObject, GestureHandler, flatGesture } from './utils'
8+
import { useTransformStyle, splitStyle, splitProps, useLayout, wrapChildren, extendObject, GestureHandler, flatGesture, useRunOnJSCallback } from './utils'
99
import { SwiperContext } from './context'
1010
import Portal from './mpx-portal'
1111
/**
@@ -148,6 +148,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
148148
autoplay = false,
149149
circular = false,
150150
disableGesture = false,
151+
current: propCurrent = 0,
151152
bindchange
152153
} = props
153154
const easeingFunc = props['easing-function'] || 'default'
@@ -198,10 +199,10 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
198199
// 每个元素的宽度 or 高度,有固定值直接初始化无则0
199200
const step = useSharedValue(initStep)
200201
// 记录选中元素的索引值
201-
const currentIndex = useSharedValue(props.current || 0)
202+
const currentIndex = useSharedValue(propCurrent)
202203
// const initOffset = getOffset(props.current || 0, initStep)
203204
// 记录元素的偏移量
204-
const offset = useSharedValue(getOffset(props.current || 0, initStep))
205+
const offset = useSharedValue(getOffset(propCurrent, initStep))
205206
const strAbso = 'absolute' + dir.toUpperCase() as StrAbsoType
206207
const strVelocity = 'velocity' + dir.toUpperCase() as StrVelocityType
207208
// 标识手指触摸和抬起, 起点在onBegin
@@ -270,7 +271,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
270271
const iStep = dir === 'x' ? realWidth : realHeight
271272
if (iStep !== step.value) {
272273
step.value = iStep
273-
updateCurrent(props.current || 0, iStep)
274+
updateCurrent(propCurrent, iStep)
274275
updateAutoplay()
275276
}
276277
}
@@ -374,7 +375,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
374375
easing: easeMap[easeingFunc]
375376
}, () => {
376377
currentIndex.value = nextIndex
377-
runOnJS(loop)()
378+
runOnJS(runOnJSCallback)('loop')
378379
})
379380
} else {
380381
// 默认向右, 向下
@@ -389,7 +390,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
389390
// 将开始位置设置为真正的位置
390391
offset.value = initOffset
391392
currentIndex.value = nextIndex
392-
runOnJS(loop)()
393+
runOnJS(runOnJSCallback)('loop')
393394
})
394395
} else {
395396
nextIndex = currentIndex.value + 1
@@ -400,7 +401,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
400401
easing: easeMap[easeingFunc]
401402
}, () => {
402403
currentIndex.value = nextIndex
403-
runOnJS(loop)()
404+
runOnJS(runOnJSCallback)('loop')
404405
})
405406
}
406407
}
@@ -428,13 +429,21 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
428429
}
429430
}, [])
430431

431-
function handleSwiperChange (current: number) {
432-
if (props.current !== currentIndex.value) {
432+
function handleSwiperChange (current: number, pCurrent: number) {
433+
if (pCurrent !== currentIndex.value) {
433434
const eventData = getCustomEvent('change', {}, { detail: { current, source: 'touch' }, layoutRef: layoutRef })
434435
bindchange && bindchange(eventData)
435436
}
436437
}
437438

439+
const runOnJSCallbackRef = useRef({
440+
loop,
441+
pauseLoop,
442+
resumeLoop,
443+
handleSwiperChange
444+
})
445+
const runOnJSCallback = useRunOnJSCallback(runOnJSCallbackRef)
446+
438447
function getOffset (index: number, stepValue: number) {
439448
if (!stepValue) return 0
440449
let targetOffset = 0
@@ -451,12 +460,12 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
451460
const targetOffset = getOffset(index || 0, stepValue)
452461
if (targetOffset !== offset.value) {
453462
// 内部基于props.current!==currentIndex.value决定是否使用动画及更新currentIndex.value
454-
if (props.current !== undefined && props.current !== currentIndex.value) {
463+
if (propCurrent !== undefined && propCurrent !== currentIndex.value) {
455464
offset.value = withTiming(targetOffset, {
456465
duration: easeDuration,
457466
easing: easeMap[easeingFunc]
458467
}, () => {
459-
currentIndex.value = props.current || 0
468+
currentIndex.value = propCurrent
460469
})
461470
} else {
462471
offset.value = targetOffset
@@ -474,7 +483,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
474483
useAnimatedReaction(() => currentIndex.value, (newIndex: number, preIndex: number) => {
475484
// 这里必须传递函数名, 直接写()=> {}形式会报 访问了未sharedValue信息
476485
if (newIndex !== preIndex && bindchange) {
477-
runOnJS(handleSwiperChange)(newIndex)
486+
runOnJS(runOnJSCallback)('handleSwiperChange', newIndex, propCurrent)
478487
}
479488
})
480489

@@ -510,10 +519,10 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
510519
useEffect(() => {
511520
// 1. 如果用户在touch的过程中, 外部更新了current以外部为准(小程序表现)
512521
// 2. 手指滑动过程中更新索引,外部会把current再传入进来,导致offset直接更新,增加判断不同才更新
513-
if (props.current !== currentIndex.value) {
514-
updateCurrent(props.current || 0, step.value)
522+
if (propCurrent !== currentIndex.value) {
523+
updateCurrent(propCurrent, step.value)
515524
}
516-
}, [props.current])
525+
}, [propCurrent])
517526

518527
useEffect(() => {
519528
autoplayShared.value = autoplay
@@ -603,7 +612,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
603612
if (touchfinish.value !== false) {
604613
currentIndex.value = selectedIndex
605614
offset.value = resetOffset
606-
runOnJS(resumeLoop)()
615+
runOnJS(runOnJSCallback)('resumeLoop')
607616
}
608617
})
609618
} else {
@@ -613,7 +622,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
613622
}, () => {
614623
if (touchfinish.value !== false) {
615624
currentIndex.value = selectedIndex
616-
runOnJS(resumeLoop)()
625+
runOnJS(runOnJSCallback)('resumeLoop')
617626
}
618627
})
619628
}
@@ -635,7 +644,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
635644
}, () => {
636645
if (touchfinish.value !== false) {
637646
currentIndex.value = moveToIndex
638-
runOnJS(resumeLoop)()
647+
runOnJS(runOnJSCallback)('resumeLoop')
639648
}
640649
})
641650
}
@@ -662,19 +671,10 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
662671
'worklet'
663672
const { diffOffset, half, isTriggerUpdateHalf } = computeHalf(eventData)
664673
if (+diffOffset === 0) {
665-
runOnJS(resumeLoop)()
674+
runOnJS(runOnJSCallback)('resumeLoop')
666675
} else if (isTriggerUpdateHalf) {
667-
// 如果触发了onUpdate时的索引变更,则直接以update时的index为准
668-
const targetIndex = !circularShared.value ? currentIndex.value : currentIndex.value + patchElmNumShared.value - 1
669-
offset.value = withTiming(-targetIndex * step.value, {
670-
duration: easeDuration,
671-
easing: easeMap[easeingFunc]
672-
}, () => {
673-
if (touchfinish.value !== false) {
674-
currentIndex.value = targetIndex
675-
runOnJS(resumeLoop)()
676-
}
677-
})
676+
// 如果触发了onUpdate时的索引变更
677+
handleEnd(eventData)
678678
} else if (half) {
679679
handleEnd(eventData)
680680
} else {
@@ -745,14 +745,14 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
745745
if (!step.value) return
746746
touchfinish.value = false
747747
cancelAnimation(offset)
748-
runOnJS(pauseLoop)()
748+
runOnJS(runOnJSCallback)('pauseLoop')
749749
preAbsolutePos.value = e[strAbso]
750750
moveTranstion.value = e[strAbso]
751751
})
752752
.onUpdate((e: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
753753
'worklet'
754-
if (touchfinish.value) return
755754
const moveDistance = e[strAbso] - preAbsolutePos.value
755+
if (touchfinish.value || moveDistance === 0) return
756756
const eventData = {
757757
translation: moveDistance,
758758
transdir: moveDistance

0 commit comments

Comments
 (0)