React Native Context报错:未找到变量queriesString求助
首先,我们来逐个排查你遇到的问题:
1. 直接引发错误的根源:App.js中未定义queriesString
在你的App.js里,你在Provider内部写了{console.log(queriesString + " - From App")},但这个queriesString变量并没有在App组件中声明过——你只是在Provider的value里传了这个属性,所以这里直接抛出了ReferenceError。
修正App.js
我们需要把查询字符串的状态管理放在App组件中,同时把状态和更新函数一起传递给Context Provider,这样子组件才能修改这个值:
import { NavigationContainer } from "@react-navigation/native"; import { createNativeStackNavigator } from "@react-navigation/native-stack"; import HomeScreen from "./app/screens/HomeScreen"; import ScannerScreen from "./app/screens/ScannerScreen"; import { queriesContext } from "./app/global/queriesContext"; import { useState } from "react"; // 引入useState const Stack = createNativeStackNavigator(); export default function App() { // 在这里创建状态,管理查询字符串 const [queriesString, setQueriesString] = useState("123456789"); // 现在可以正常访问queriesString了 console.log(queriesString + " - From App"); return ( <> {/* 把状态和更新函数一起传给Context */} <queriesContext.Provider value={{ queriesString, setQueriesString }}> <NavigationContainer> <Stack.Navigator initialRouteName='Home' screenOptions={{ headerShown: false }}> <Stack.Screen name='Home' component={HomeScreen} /> <Stack.Screen name='Scanner' component={ScannerScreen} /> </Stack.Navigator> </NavigationContainer> </queriesContext.Provider> </> ); }
2. Context使用方式错误
在HomeScreen.js里,你尝试用数组解构const [queriesString, setQueriesString] = useContext(queriesContext);来获取上下文值,但Context传递的是一个对象(包含queriesString和setQueriesString属性),不是数组,所以这种写法是错误的。
修正HomeScreen.js
import { StyleSheet, Text, View, Image, SafeAreaView, Platform, TextInput, Button, TouchableWithoutFeedback, Keyboard } from "react-native"; import { useNavigation } from "@react-navigation/native"; import React, { useContext, useState } from "react"; import ProductScreen from "../global/GetData"; import { queriesContext } from "../global/queriesContext"; export default function HomeScreen() { // 解构Context中的值,而不是数组解构 const { queriesString, setQueriesString } = useContext(queriesContext); const [queries, setQueries] = useState(queriesString); const navigation = useNavigation(); const handleSearch = () => { setQueriesString(queries); }; // 移除直接调用ProductScreen()的代码,只在JSX中渲染组件 return ( <TouchableWithoutFeedback onPress={Keyboard.dismiss}> <SafeAreaView style={styles.MainScreen}> <ProductScreen /> {/* TextInput用onChangeText而不是onChange,直接获取输入文本 */} <TextInput style={styles.TextField} editable={true} maxLength='30' numberOfLines='1' placeholder='Art no.' keyboardType='numeric' onChangeText={(text) => setQueries(text)} // 这里改成onChangeText value={queries} // 添加value绑定,让输入框和state同步 /> <Button color='red' title='Search' onPress={handleSearch} /> <View style={styles.BarcodeBox}> <TouchableWithoutFeedback onPress={() => navigation.navigate("Scanner")}> <Image style={styles.BarcodeImage} source={require("../assets/barcode.png")} /> </TouchableWithoutFeedback> </View> </SafeAreaView> </TouchableWithoutFeedback> ); } const styles = StyleSheet.create({ MainScreen: { /* 你的样式 */ }, TextField: { /* 你的样式 */ }, BarcodeBox: { /* 你的样式 */ }, BarcodeImage: { /* 你的样式 */ }, });
3. GetData.js中API请求未响应查询字符串变化
你的useEffect依赖数组是空的,所以只会在组件挂载时调用一次API,当queriesString变化时不会重新请求。另外,你在then里直接写console.log(data),这时候data还没被更新(因为setData是异步的),应该放在另一个useEffect里监听data变化。
修正GetData.js
import { StyleSheet, Text, View, Image, SafeAreaView, TouchableWithoutFeedback, Keyboard } from "react-native"; import React, { useContext, useState, useEffect } from "react"; import { queriesContext } from "./queriesContext"; export default function ProductScreen() { // 这里改成标准的函数声明,避免变量提升问题 const { queriesString } = useContext(queriesContext); const [isLoading, setLoading] = useState(true); const [isPromotion, setPromotion] = useState(false); const [data, setData] = useState({ results: [{ hits: [{ name: { no: "" }, description: { no: "" }, potentialPromotionsLabels: {}, code: "", ean: "" }] }] // 初始化一个安全的结构,避免渲染时报错 }); console.log(queriesString + " - From GetData"); useEffect(() => { // 当queriesString变化时重新请求API,所以把它加入依赖数组 setLoading(true); // 请求开始时设置为加载中 fetch( "SERVER.........", // 替换成你的实际API地址 { method: "POST", headers: { Accept: "application/json", "Content-Type": "application/json", "x-algolia-agent": "Algolia for JavaScript (4.13.1); Browser; JS Helper (3.10.0)", "x-algolia-application-id": "***********", "x-algolia-api-key": "************************", }, body: JSON.stringify({ requests: [ { indexName: "prod_products", params: `query=${queriesString}`, // 用模板字符串更清晰 }, ], }), } ) .then((response) => response.json()) // 修复拼写错误:respnse → response .then((json) => { setData(json); console.log(json); // 直接打印返回的json,而不是还没更新的data }) .catch((error) => console.error(error)) .finally(() => setLoading(false)); }, [queriesString]); // 添加queriesString到依赖数组 // 监听data变化,可选 useEffect(() => { if (!isLoading) { console.log(data); } }, [data, isLoading]); return ( <TouchableWithoutFeedback onPress={Keyboard.dismiss}> <SafeAreaView style={styles.ProductScreen}> <View style={styles.LogoBox}> <Image source={require("../assets/Logo.png")} /> <Image style={styles.ProductImage} resizeMode='contain' source={ isLoading ? require("../assets/fallback_266x266.png") : { uri: `https://................/${data.results[0].hits[0].picture.product}`, // 模板字符串 } } /> </View> <View> <View> <Text style={styles.Title}> {data.results[0].hits[0].name.no} </Text> <Text style={styles.Desciption}>{data.results[0].hits[0].description.no}</Text> {/* 先判断potentialPromotionsLabels是否存在,避免报错 */} {data.results[0].hits[0].potentialPromotionsLabels?.[5110] && ( <Text style={[styles.Text, styles.color]}>{data.results[0].hits[0].potentialPromotionsLabels[5110].text.no}</Text> )} <Text style={styles.Text}> Price: {data.results[0].hits[0].code}</Text> <Text style={styles.Text}> Article No: {data.results[0].hits[0].code}</Text> <Text style={styles.Text}> EAN: {data.results[0].hits[0].ean}</Text> </View> </View> </SafeAreaView> </TouchableWithoutFeedback> ); }; const styles = StyleSheet.create({ color: { /* 你的样式 */ }, ProductScreen: { /* 你的样式 */ }, LogoBox: { /* 你的样式 */ }, ProductImage: { /* 你的样式 */ }, Title: { /* 你的样式 */ }, Desciption: { /* 你的样式 */ }, Text: { /* 你的样式 */ }, });
4. ScannerScreen扫描后更新查询字符串
现在我们可以在ScannerScreen中使用Context,扫描到条码后直接更新查询字符串,然后返回HomeScreen:
修正ScannerScreen.js
import React, { useState, useEffect, useContext } from "react"; import { Text, View, Alert, StyleSheet } from "react-native"; import { BarCodeScanner } from "expo-barcode-scanner"; import { useNavigation } from "@react-navigation/native"; import { queriesContext } from "../global/queriesContext"; // 引入Context export default function ScannerScreen() { const [hasPermission, setHasPermission] = useState(null); const [scanned, setScanned] = useState(false); const { setQueriesString } = useContext(queriesContext); // 获取更新函数 const navigation = useNavigation(); useEffect(() => { const getBarCodeScannerPermissions = async () => { const { status } = await BarCodeScanner.requestPermissionsAsync(); setHasPermission(status === "granted"); }; getBarCodeScannerPermissions(); }, []); const handleBarCodeScanned = ({ type, data }) => { setScanned(true); Alert.alert("Strekkode", data, [ { text: "Yes", onPress: () => { setQueriesString(data); // 更新查询字符串 navigation.goBack(); // 返回HomeScreen } }, { text: "No", onPress: () => setScanned(false) } ]); }; if (hasPermission === null) { return <Text>Requesting for camera permission</Text>; } if (hasPermission === false) { return <Text>No access to camera</Text>; } return ( <BarCodeScanner onBarCodeScanned={scanned ? undefined : handleBarCodeScanned} style={[StyleSheet.absoluteFill, styles.container]}> <View style={styles.layerTop} /> <View style={styles.layerCenter}> <View style={styles.layerLeft} /> <View style={styles.focused} /> <View style={styles.layerRight} /> </View> <View style={styles.layerBottom} /> </BarCodeScanner> ); } const opacity = "rgba(0, 0, 0, .5)"; const styles = StyleSheet.create({ container: { flex: 1, flexDirection: "column", }, layerTop: { flex: 2, backgroundColor: opacity, }, layerCenter: { flex: 1, flexDirection: "row", }, layerLeft: { flex: 1, backgroundColor: opacity, }, focused: { flex: 10, }, layerRight: { flex: 1, backgroundColor: opacity, }, layerBottom: { flex: 2, backgroundColor: opacity, }, });
总结一下关键的修复点:
- 在App组件中管理Context的状态,并传递状态和更新函数
- 修正Context的解构方式,从对象中获取值而不是数组解构
- 修复TextInput的
onChange为onChangeText,确保正确获取输入文本 - 为GetData的useEffect添加
queriesString依赖,实现查询字符串变化时重新请求API - 为ScannerScreen添加Context调用,扫描后更新查询字符串并返回主页
- 为GetData的data初始化安全的结构,避免渲染时出现空指针错误
这样应该就能解决你遇到的ReferenceError,同时让整个应用的状态流转正常工作了。
内容的提问来源于stack exchange,提问作者EXPZ

