React Native中BottomSheet的useRef如何避免为null?扫码触发弹窗问题
解决扫码后BottomSheet引用为null及无法再次扫码的问题
问题根源
- BottomSheet引用为null:你的代码中BottomSheet仅在
scanned为true时才渲染,扫码后调用setScanned(true)触发组件重渲染,但BottomSheet此时还未完成挂载,直接调用sheetRef.current.snapTo(0)自然拿不到实例。即使使用useEffect监听product或scanned状态,也可能因为组件渲染周期的问题,在ref赋值前就执行了回调。 - 无法再次扫码:
scanned设为true后,BarCodeScanner的onBarCodeScanned被设为undefined,扫码器直接失效,且没有重置scanned的逻辑,导致无法再次扫码。
修正方案
- 让BottomSheet始终挂载在DOM中,通过
index属性控制显示/隐藏(index=-1为关闭状态),确保sheetRef始终能获取到实例。 - 在BottomSheet关闭时(比如用户下滑关闭),重置
scanned状态,恢复扫码器的功能。 - 扫码成功后直接调用
snapTo(0)展开BottomSheet,无需等待状态更新后的副作用。
修改后的完整代码
const ScanScreen = ({ navigation }) => { const width = Dimensions.get("window").width; const height = Dimensions.get("window").height; const sheetRef = useRef(null); const [scanned, setScanned] = useState(false); const [product, setProduct] = useState({ name: "" }); // 渲染BottomSheet内容,避免组件重复创建函数 const renderProductModal = () => ( <View style={{ height: 450 }}> <Text>Product: {product.name}. Swipe down to close.</Text> </View> ); const handleScannedCodeBar = async ({ type, data }) => { if (!data) return; setProduct({ name: "Mon produit scanné" }); // 直接调用snapTo,BottomSheet已常驻挂载,ref不为null sheetRef.current?.snapTo(0); // 临时禁用扫码,避免重复触发 setScanned(true); }; // BottomSheet关闭时重置扫码状态 const handleSheetClose = () => { setScanned(false); }; return ( <View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}> <BarCodeScanner onBarCodeScanned={scanned ? undefined : handleScannedCodeBar} style={{ height: height, width: width }} /> {/* 始终渲染BottomSheet,用index控制显示 */} <SafeAreaView> <BottomSheet ref={sheetRef} index={-1} // 默认关闭 snapPoints={[450, 300, 0]} borderRadius={10} renderContent={renderProductModal} // 监听BottomSheet关闭完成事件 onCloseEnd={handleSheetClose} /> </SafeAreaView> </View> ); }; export default ScanScreen;
关键修改说明
- BottomSheet常驻渲染:移除
{scanned && ...}的条件渲染,让BottomSheet始终存在,确保sheetRef能稳定获取组件实例。 - 扫码逻辑调整:扫码成功后先设置产品数据,再调用
snapTo(0)展开BottomSheet,最后设置scanned=true临时禁用扫码。 - 重置扫码状态:通过
onCloseEnd监听BottomSheet关闭事件,在关闭完成时将scanned设为false,恢复扫码器功能。 - 性能优化:将
productModal改为renderProductModal并移到状态定义下方,避免组件每次渲染都重新创建函数。
内容的提问来源于stack exchange,提问作者Em.
相关产品推荐
相关产品推荐

