React Native圣经应用动态API调用及页面刷新问题求助
问题分析与解决方案
核心问题1:route.params未定义
代码错误点:
- Verse.js未正确获取路由参数:你注释掉了
const { verseReference } = route.params;,但在JSX模板中直接使用{ verseReference },导致变量未定义报错。 - Card.js错误使用类组件语法:在函数组件里写
this.state = { verseReference: "Test" };,函数组件不支持this.state,这行代码无效且会引发运行时错误。 - Card.js异步state更新用法错误:
setSelectedVerseReference(verseReference)后立即console.log(selectedVerseReference),由于useState是异步更新,这里打印的始终是旧值,没有实际意义。
修复方案:
- 移除Card.js中的
this.state代码块。 - 在Verse.js中正确获取路由参数,添加可选链避免参数未定义时的报错。
核心问题2:切换经文后详情页不重新加载
代码错误点:
- 未监听路由参数变化:仅在组件挂载时读取一次AsyncStorage和调用API,从Card页面切换不同经文导航过来时,路由参数变化但组件无响应。
- AsyncStorage冗余使用:已经通过路由传递参数,没必要再用AsyncStorage中转,反而增加状态同步复杂度。
- useEffect依赖错误:第二个useEffect依赖
selectedVerseReference,但该state从未被赋值,永远不会触发;第一个useEffect依赖空数组,仅执行一次。 - API请求存在语法错误:Verse.js的fetch请求中引用了未定义的
data变量,会导致请求失败。
修复方案:
- 直接使用路由参数作为API请求依据,移除AsyncStorage的冗余逻辑。
- 监听
route.params变化,参数更新时重新调用API。 - 修正API请求错误,移除未定义的
body参数(GET请求无需body)。
修复后的Card.js代码
import React from "react"; import { StyleSheet, TouchableWithoutFeedback } from 'react-native'; import { Block, Text, theme } from 'galio-framework'; import { argonTheme } from '../constants'; import PropTypes from 'prop-types'; import { useNavigation } from '@react-navigation/native'; const Card = ({ item, horizontal, full, style, ctaColor, imageStyle }) => { const navigation = useNavigation(); const viewVerse = (verseReference) => { // 直接通过路由传递参数,无需AsyncStorage中转 navigation.navigate(item.link, { verseReference }); } const imageStyles = [ full ? styles.fullImage : styles.horizontalImage, imageStyle ]; const cardContainer = [styles.card, styles.shadow, style]; const imgContainer = [ styles.imageContainer, horizontal ? styles.horizontalStyles : styles.verticalStyles, styles.shadow ]; return ( <Block row={horizontal} card flex style={cardContainer}> <TouchableWithoutFeedback onPress={() => console.log('OK Pressed')}> <Block flex style={imgContainer}></Block> </TouchableWithoutFeedback> <TouchableWithoutFeedback onPress={() => viewVerse(item.kjv_verse_keyphrase)}> <Block flex space="between" style={styles.cardDescription}> <Text size={16} style={styles.cardTitle}>{item.title}</Text> <Text size={12} muted={!ctaColor} color={ctaColor || argonTheme.COLORS.ACTIVE} bold>{item.link}</Text> <Text size={12} muted={!ctaColor} color={ctaColor || argonTheme.COLORS.ACTIVE} bold>{item.kjv_verse_keyphrase}</Text> </Block> </TouchableWithoutFeedback> </Block> ); } Card.propTypes = { item: PropTypes.object, navigation: PropTypes.object, horizontal: PropTypes.bool, full: PropTypes.bool, ctaColor: PropTypes.string, imageStyle: PropTypes.any, } // 保留你的样式代码 const styles = StyleSheet.create({ card: { // 原样式内容 }, // 其他样式... }); export default Card;
修复后的Verse.js代码
import React, { useEffect, useState } from "react"; import { useNavigation, useRoute } from "@react-navigation/native"; import { View, StyleSheet, Dimensions, ScrollView, Image, ImageBackground, ActivityIndicator } from "react-native"; import { Block, Text, theme } from "galio-framework"; import { Button } from "../components"; import { Images, argonTheme } from "../constants"; import { HeaderHeight } from "../constants/utils"; import RenderHtml from 'react-native-render-html'; // Environment & Screen import { REACT_APP_KJV_API_KEY } from '@env'; const { width, height } = Dimensions.get("screen"); const thumbMeasure = (width - 48 - 32) / 3; const Verse = () => { const navigation = useNavigation(); const route = useRoute(); // 从路由参数获取经文引用,添加可选链避免参数未定义报错 const { verseReference } = route.params || {}; const [isLoading, setIsLoading] = useState(true); const [verseContent, setVerseContent] = useState(null); // 监听路由参数变化,参数更新时重新请求API useEffect(() => { if (!verseReference) return; const fetchVerseData = async () => { setIsLoading(true); try { const fullURLstructure = constructApiUrl(verseReference); console.log("Full URL Structure: " + fullURLstructure); const response = await fetch(fullURLstructure, { headers:{ 'api-key': REACT_APP_KJV_API_KEY, }, }); if (!response.ok) throw new Error('API请求失败'); const data = await response.json(); setVerseContent(data); } catch (error) { console.error('Error fetching verse data:', error); } finally { setIsLoading(false); } }; fetchVerseData(); }, [verseReference]); // 依赖路由参数,参数变化时触发请求 const constructApiUrl = (verseValue) => { const baseBibleURL = 'https://api.bibliosexample.com/v3/xljsnfuoalkls/passages/'; return `${baseBibleURL}${verseValue}`; }; if (isLoading) { return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <ActivityIndicator size="large" /> </View> ); } if (!verseContent?.data) { return ( <View style={{ flex:1, justifyContent: 'center', alignItems: 'center' }}> <Text>Verse data not found.</Text> </View> ); } const source = { html: verseContent.data.content }; return ( <Block flex style={styles.profile}> <Block flex> <ImageBackground source={Images.ProfileBackground} style={styles.profileContainer} imageStyle={styles.profileBackground}> <ScrollView showsVerticalScrollIndicator={false} style={{ width, marginTop: '10%' }} > <Block flex style={styles.profileCard}> <Block flex> <Block middle style={styles.nameInfo}> <Text bold size={28} color="#32325D">{ verseReference || 'Unknown Verse' }</Text> <Text size={16} color="#32325D" style={{ marginTop: 10 }}>{verseContent.data.reference}</Text> </Block> <Block middle style={{ marginTop: 30, marginBottom: 16 }}><Block style={styles.divider} /></Block> <Block middle> <RenderHtml contentWidth={width} source={source}/> <Button color="transparent" textStyle={{color: "#233DD2",fontWeight: "500",fontSize: 16}}> Read Full Verse </Button> </Block> </Block> </Block> </ScrollView> </ImageBackground> </Block> </Block> ); }; // 保留你的样式代码 const styles = StyleSheet.create({ profile: { // 原样式内容 }, // 其他样式... }); export default Verse;
内容的提问来源于stack exchange,提问作者Travis Miller
相关产品推荐
相关产品推荐

