Expo项目expo-barcode-scanner扫码无返回结果问题排查
问题排查结论
扫码无结果是代码逻辑错误导致,和expo-barcode-scanner模块、权限配置无关,核心问题如下:
handleBarCodeScanned事件回调未接收扫码返回的{type, data}参数,且错误地将UI渲染逻辑写在事件回调内——事件回调仅负责处理触发后的业务逻辑,不能直接return组件,所有UI内容必须写在组件的render返回结构中。- 从Context解构取值时遗漏了
getProducts方法,扫码成功后根本没有触发商品查询接口。 - 列表渲染的
products.map箭头函数未return组件,即使有数据也无法渲染;同时OpenFoodFacts接口返回结构无res.data.items字段,取值路径错误,该接口默认返回单个商品对象而非商品数组。 - 根容器未设置
flex:1样式,部分机型上扫码预览区域尺寸异常,无法正常识别条码。
修正后代码
1. ScanScreen.js
import React, { useState, useEffect, useContext } from 'react'; import { Text, View, StyleSheet, Button } from 'react-native'; import { BarCodeScanner } from 'expo-barcode-scanner'; import OpenFoodContext from '../../context/openfood/openfoodContext' import Spinner from '../../components/Spinner'; const ScanScreen = () => { // 从Context中解构出查询商品的方法 const openFoodContext = useContext(OpenFoodContext); const { products, loading, getProducts } = openFoodContext; const [hasPermission, setHasPermission] = useState(null); const [scanned, setScanned] = useState(false); const [scanResult, setScanResult] = useState(''); useEffect(() => { (async () => { const { status } = await BarCodeScanner.requestPermissionsAsync(); setHasPermission(status === 'granted'); })(); }, []); // 扫码回调必须接收扫描返回的类型、条码值参数 const handleBarCodeScanned = ({ type, data }) => { setScanned(true); setScanResult(`识别到条码:${data},类型:${type}`); // 传入扫到的条码查询商品信息 getProducts(data); }; if (hasPermission === null) { return <Text>正在申请相机权限</Text>; } if (hasPermission === false) { return <Text>未获得相机访问权限,请在系统设置中开启</Text>; } return ( <View style={styles.container}> <BarCodeScanner onBarCodeScanned={scanned ? undefined : handleBarCodeScanned} style={StyleSheet.absoluteFillObject} /> {/* 所有UI渲染逻辑放在JSX结构中,不要写在事件回调里 */} {loading ? <Spinner /> : ( <View style={styles.resultArea}> <Text style={styles.scanText}>{scanResult}</Text> {products.length > 0 && products.map((product, index) => ( // map渲染列表必须return组件,同时添加唯一key <Text key={index} style={styles.productText}>{product?.product_name || '未查询到商品名'}</Text> ))} </View> )} {scanned && !loading && ( <Button title={'点击重新扫码'} onPress={() => { setScanned(false); setScanResult(''); }} /> )} </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, position: 'relative' }, resultArea: { position: 'absolute', bottom: 120, left: 20, right: 20, backgroundColor: 'rgba(0,0,0,0.7)', padding: 16, borderRadius: 8 }, scanText: { color: '#fff', fontSize: 16, marginBottom: 12 }, productText: { color: '#fff', fontSize: 14 } }) export default ScanScreen;
2. OpenFoodState.js
import React, {useReducer} from 'react'; import axios from 'axios'; import OpenFoodContext from './openfoodContext'; import OpenFoodReducer from './openfoodReducer'; import { GET_ITEMS, SET_LOADING, } from '../types'; const OpenFoodState = props => { const initialState ={ products: [], loading: false } const [state, dispatch] = useReducer(OpenFoodReducer, initialState); // 接收扫码得到的条码作为查询参数 const getProducts = async (barcode) => { setLoading(); try { const res = await axios.get( `https://world.openfoodfacts.org/api/v0/product/${barcode}.json` ); // 修正接口取值路径,该接口返回单个商品对象,包装为数组适配列表渲染逻辑 const productData = res.data.product ? [res.data.product] : []; dispatch({ type: GET_ITEMS, payload: productData }); } catch (err) { console.log('商品查询失败', err); dispatch({ type: GET_ITEMS, payload: [] }) } }; const setLoading = () => dispatch ({type: SET_LOADING}); return <OpenFoodContext.Provider value={{ products: state.products, loading: state.loading, getProducts }} > {props.children} </OpenFoodContext.Provider> } export default OpenFoodState;
3. openfoodReducer
原有逻辑无错误,无需修改。
内容的提问来源于stack exchange,提问作者mahnuel
相关产品推荐
相关产品推荐

