升级react-native-gesture-handler至v2后手势内setState卡顿求助
react-native-gesture-handler v2手势更新状态卡顿的解决方法
核心原因
在onUpdate这种高频回调中频繁调用setState会触发组件多次重渲染,v2版本对手势回调的执行机制做了调整,导致这种重渲染的卡顿表现比v1更明显。
有效解决方法
优先使用
Animated.Value驱动样式Animated.Value可以直接在原生线程处理动画更新,无需触发React组件重渲染,是解决这类高频更新卡顿的最优方案:// 初始化Animated值 const headerMarginTop = useRef(new Animated.Value(initialHeaderMarginTop)).current; const contentMarginTop = useRef(new Animated.Value(initialContentMarginTop)).current; const panGesture = Gesture.Pan() .runOnJS(true) .onUpdate((e) => { const offsetY = e.translationY; if (scrollIsTop && offsetY > 0) { headerMarginTop.setValue(initialHeaderMarginTop + offsetY); contentMarginTop.setValue(initialContentMarginTop + offsetY); } }) .simultaneousWithExternalGesture(scrollRef);组件中通过
Animated.style绑定样式:return ( <SwipeUpDownModal modalVisible={visible} PressToanimate={animateModal} HeaderStyle={Animated.style({ marginTop: headerMarginTop })} ContentModalStyle={Animated.style({ marginTop: contentMarginTop })} ContentModal={ <GestureDetector gesture={panGesture}> <ScrollView ref={scrollRef} style={styles.modalContent} scrollEventThrottle={16} onScroll={checkOverScroll} bounces={false} > ... </ScrollView> </GestureDetector> </SwipeUpDownModal> )合并状态更新(若必须使用React状态)
如果依赖React状态无法切换到Animated,可以将两个独立状态合并为一个对象,减少重渲染触发次数:const [margins, setMargins] = useState({ header: initialHeaderMarginTop, content: initialContentMarginTop }); const panGesture = Gesture.Pan() .runOnJS(true) .onUpdate((e) => { const offsetY = e.translationY; if (scrollIsTop && offsetY > 0) { setMargins({ header: initialHeaderMarginTop + offsetY, content: initialContentMarginTop + offsetY }); } }) .simultaneousWithExternalGesture(scrollRef);样式中直接使用合并后的状态:
HeaderStyle={{ marginTop: margins.header }} ContentModalStyle={{ marginTop: margins.content }}节流高频回调
对onUpdate的状态更新逻辑做节流处理,限制更新频率(比如每16ms一次,对应60fps):import { throttle } from 'lodash'; const updateMargins = useCallback(throttle((offsetY) => { if (scrollIsTop && offsetY > 0) { setHeaderMarginTop(initialHeaderMarginTop + offsetY); setContentMarginTop(initialContentMarginTop + offsetY); } }, 16), [scrollIsTop]); const panGesture = Gesture.Pan() .runOnJS(true) .onUpdate((e) => { updateMargins(e.translationY); }) .simultaneousWithExternalGesture(scrollRef);优化
runOnJS的使用
检查是否必须在JS线程执行回调,如果scrollIsTop可以通过原生手势判断或不需要访问JS状态,去掉runOnJS(true),让手势逻辑在原生线程执行,减轻JS线程压力。
内容的提问来源于stack exchange,提问作者user1666858
相关产品推荐
相关产品推荐

