使用useAnimatedScrollHandler与BottomSheetScrollView时触发ReanimatedError求助
问题描述
使用react-native-reanimated的useAnimatedScrollHandler绑定@gorhom/bottom-sheet的BottomSheetScrollView的onScroll事件时,触发以下错误:
ReanimatedError: Exception in HostFunction: [Reanimated] Attempted to extract from a HostObject that wasn't converted to a Shareable., js engine: reanimated
改用React Native原生Animated API时,同样触发该错误。
报错原因
BottomSheetScrollView的onScroll事件回调参数并非Reanimated期望的原生滚动事件对象,而是经过@gorhom/bottom-sheet封装后的自定义对象,原生事件嵌套在nativeEvent属性中。直接将useAnimatedScrollHandler返回的处理函数绑定到onScroll,Reanimated无法解析封装后的对象,进而抛出HostObject转换错误。
解决方案
方法一:使用@gorhom/bottom-sheet内置的滚动控制器(推荐)
@gorhom/bottom-sheet提供了useBottomSheetScrollableController钩子,专门用于获取内部滚动组件的状态(包括滚动偏移量),无需手动处理滚动事件:
import React, { memo } from "react"; import { Text, VStack } from "@gluestack-ui/themed"; import { BottomSheetScrollView, useBottomSheetScrollableController } from "@gorhom/bottom-sheet"; import { useSharedValue, useAnimatedReaction } from "react-native-reanimated"; interface IWheelPickerProps { data: { value: string; label: string }[]; contentClassname?: string; } export const WheelPicker = memo(({ data, contentClassname }: IWheelPickerProps) => { const scrollY = useSharedValue(0); const scrollableController = useBottomSheetScrollableController(); // 监听滚动偏移变化,同步更新scrollY useAnimatedReaction( () => scrollableController.scrollableContentOffset.value.y, (newY) => { scrollY.value = newY; } ); return ( <VStack className={`flex-1 items-center ${contentClassname}`}> <BottomSheetScrollView scrollableController={scrollableController}> {data.map((item) => ( <VStack className="flex-1 items-start justify-center" key={item.value}> <Text bold className="self-center text-xl">{item.label}</Text> </VStack> ))} </BottomSheetScrollView> </VStack> ); });
方法二:手动提取原生滚动事件对象
如果不想使用内置控制器,可以在onScroll回调中手动提取嵌套的原生事件对象,再传递给Reanimated的滚动处理函数:
import React, { memo } from "react"; import { Text, VStack } from "@gluestack-ui/themed"; import { BottomSheetScrollView } from "@gorhom/bottom-sheet"; import { useSharedValue, useAnimatedScrollHandler } from "react-native-reanimated"; interface IWheelPickerProps { data: { value: string; label: string }[]; contentClassname?: string; } export const WheelPicker = memo(({ data, contentClassname }: IWheelPickerProps) => { const scrollY = useSharedValue(0); const animatedScrollHandler = useAnimatedScrollHandler({ onScroll: ({ contentOffset: { y } }) => { scrollY.value = y; }, }); // 包装回调,提取原生事件传递给Reanimated处理函数 const handleScroll = (event: any) => { animatedScrollHandler(event.nativeEvent); }; return ( <VStack className={`flex-1 items-center ${contentClassname}`}> <BottomSheetScrollView onScroll={handleScroll}> {data.map((item) => ( <VStack className="flex-1 items-start justify-center" key={item.value}> <Text bold className="self-center text-xl">{item.label}</Text> </VStack> ))} </BottomSheetScrollView> </VStack> ); });
内容的提问来源于stack exchange,提问作者Юрий Архипов

