React Native:如何将全局状态传入useAnimatedGestureHandler的OnStart钩子
React Native 可拖拽Tile组件层级控制问题
需求:实现多个可拖拽Tile,每次拖拽或点击时,将全局最高z轴值加1并赋值给当前操作的Tile,使其始终处于层级最上层。当前在父组件通过useState维护maxZ状态,但无法在子组件的useAnimatedGestureHandler的onStart钩子中正确更新并同步该值——useState运行在JS线程,而手势钩子运行在UI线程,尝试useSharedValue、runOnJS等跨线程通信方法未成功。
解决方案
核心思路
将全局层级状态改用useSharedValue维护(支持跨线程访问),配合runOnJS实现UI线程到JS线程的状态更新,同时通过useDerivedValue确保子组件能实时获取最新的全局层级值。
修改步骤
- 父组件替换
useState为useSharedValue来维护maxZ,并定义全局更新方法 - 将
maxZ共享值和更新方法传递给子组件 - 子组件在手势
onStart钩子中,先调用全局更新方法让maxZ自增,再将最新的maxZ赋值给当前Tile的currentZ - 用
useDerivedValue监听maxZ变化,确保子组件能实时同步全局层级状态
修改后的完整代码
import React from 'react'; import { StyleSheet, Text, View } from 'react-native'; import Animated, { useSharedValue, useAnimatedStyle, useAnimatedGestureHandler, useDerivedValue, runOnJS } from 'react-native-reanimated'; import { PanGestureHandler } from 'react-native-gesture-handler'; const tileWidth = 100; const tileHeight = 60; export default function App() { // 用useSharedValue替代useState,支持跨线程访问 const maxZ = useSharedValue(0); // 全局更新maxZ的方法,运行在JS线程 const updateMaxZ = () => { maxZ.value += 1; }; const DraggableTile = ({ txt }) => { const translateX = useSharedValue(0); const translateY = useSharedValue(0); const currentZ = useSharedValue(0); // 监听全局maxZ变化,确保当前组件能获取最新值 useDerivedValue(() => { currentZ.value = maxZ.value; }); const panGestureEvent = useAnimatedGestureHandler({ onStart: (event, context) => { // 在UI线程中调用JS线程的更新方法 runOnJS(updateMaxZ)(); context.x = translateX.value; context.y = translateY.value; }, onActive: (event, context) => { translateX.value = event.translationX + context.x; translateY.value = event.translationY + context.y; }, onEnd: () => {} }); const repositionedStyle = useAnimatedStyle(() => { return { transform: [{ translateX: translateX.value }, { translateY: translateY.value }], zIndex: currentZ.value }; }); return ( <PanGestureHandler onGestureEvent={panGestureEvent}> <Animated.View style={[styles.tile, repositionedStyle]}> <Text style={styles.tileText}>{txt}</Text> </Animated.View> </PanGestureHandler> ); }; return ( <View style={styles.container}> <DraggableTile txt="101" /> <DraggableTile txt="102" /> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#ddd', alignItems: 'center', justifyContent: 'center', }, tile: { width: tileWidth, height: tileHeight, backgroundColor: "#fff", borderWidth: 1, borderColor: "#aaa", display:"inline-flex", flexDirection:"row", alignItems:"center", justifyContent:"center" }, tileText: { fontSize:28, fontWeight:600 } });
内容的提问来源于stack exchange,提问作者Jonathan Atkinson
相关产品推荐
相关产品推荐

