React Native组件Fetch请求获取代币详情空值异常问题求助
加密代币React Native应用详情页崩溃问题排查与解决
功能逻辑
列表页展示加密代币列表,点击任意代币跳转至详情页展示该代币的详细信息。
相关代码
1. 代币列表组件(TokenList)
import { useContext, useEffect, useState } from 'react' import { Alert, FlatList, View } from 'react-native' import { SettingsContext } from '../../contexts/SettingsContext' import ItemToken from '../ItemToken/ItemToken' export function TokenList() { const { currency } = useContext(SettingsContext) const [listToken, setListToken] = useState(null) const [refreshing, setRefreshing] = useState(false) useEffect(() => { getListTokens() }, [currency]) useEffect(() => { if (refreshing) { getListTokens() setRefreshing(false) } }, [refreshing]) /** * For the currency, never mind, it's just Boolean to get things done, it's not the final code. */ function getListTokens() { fetch(`https://api.coingecko.com/api/v3/coins/markets?vs_currency=${ currency ? 'usd' : 'eur' }&order=market_cap_desc&per_page=100`) .then(response => response.json()) .then(json => setListToken(json)) .catch(error => Alert.alert('CoinGecko API Error', error)) } return ( <FlatList data={listToken} keyExtractor={item => item.id} onRefresh={() => setRefreshing(true)} refreshing={refreshing} renderItem={ ({ item }) => <ItemToken item={item} /> } ItemSeparatorComponent={<View style={{ height: 1, opacity: 0.6 }} />} /> ) }
2. 列表项组件(ItemToken,不含CSS)
import { useContext, memo } from 'react' import { View, Text, Image, TouchableOpacity, StyleSheet } from 'react-native' import { SettingsContext } from '../../contexts/SettingsContext' import { NavigationContext } from '@react-navigation/native' function ItemToken({ item }) { const { currency } = useContext(SettingsContext) const { navigation } = useContext(NavigationContext) return ( <TouchableOpacity style={styles.button} onPress={() => navigation.navigate('StackTokenDetail', { id: [item.id] })}> <View> <Image style={styles.image} src={item.image} /> </View> <View style={styles.mainSection}> <Text style={styles.fontStyle}>{item.name}</Text> <Text>{currency ? '$' : null}{item.current_price}{currency ? null : '€'}</Text> </View> </TouchableOpacity> ) }
3. 详情页容器组件(StackTokenDetail)
import { useEffect, useState } from 'react' import { SafeAreaView, ScrollView } from 'react-native' import TokenDetail from '../../TokenDetail/TokenDetail' export function StackTokenDetail({ route }) { const [token, setToken] = useState(null) useEffect(() => { fetch(`https://api.coingecko.com/api/v3/coins/${route.params.id}`) .then(response => response.json()) .then(json => setToken(json)) .catch(e => console.error(e)) }, []) return ( <SafeAreaView> <ScrollView style={{ flex: 1, padding: 40 }}> <TokenDetail token={token} /> </ScrollView> </SafeAreaView> ) }
4. 详情渲染组件(TokenDetail)
import { memo } from 'react' import { Text } from 'react-native' function TokenDetail({ token }) { console.log('tokenDetail token:', token.name) console.log('tokenDetail token:', token.symbol.toUpperCase()) console.log('tokenDetail token:', token.description.en) return ( <> <Text style={{ fontWeight: 600, paddingBottom: 20 }}>{token.symbol.toUpperCase()} - {token.name}</Text> <Text>{token.description.en}</Text> </> ) } export default memo(TokenDetail)
问题现象
- 首次点击列表项进入详情页时,报错:
Cannot read property 'name' of null - 删除
TokenDetail组件中所有访问token属性的代码并刷新,重新添加代码后再刷新,可正常获取并显示数据,但返回列表页再次点击任意代币时又会崩溃。
原因分析
- 初始值为空导致渲染错误:
StackTokenDetail中token初始值为null,但TokenDetail直接访问token.name、token.symbol等属性,组件挂载时API请求未完成,此时token为空,触发属性读取错误。 - 导航传参格式错误:
ItemToken跳转时传递的id是数组格式[item.id],但StackTokenDetail直接用该值拼接API地址,导致请求地址异常(如coins/[bitcoin]),返回数据结构不符合预期。 - 无加载/错误状态处理:组件未在API请求过程中设置加载提示或空状态,直接尝试渲染未就绪的数据。
解决方案
1. 修复导航传参格式
修改ItemToken的跳转代码,传递字符串类型的id:
// ItemToken组件中onPress回调 onPress={() => navigation.navigate('StackTokenDetail', { id: item.id })}
2. 为TokenDetail添加空值判断与加载状态
在TokenDetail中先判断token是否存在,再访问其属性:
function TokenDetail({ token }) { if (!token) { return <Text>加载中...</Text>; } console.log('tokenDetail token:', token.name) console.log('tokenDetail token:', token.symbol.toUpperCase()) console.log('tokenDetail token:', token.description.en) return ( <> <Text style={{ fontWeight: 600, paddingBottom: 20 }}>{token.symbol.toUpperCase()} - {token.name}</Text> <Text>{token.description.en}</Text> </> ) }
3. 优化StackTokenDetail的API请求逻辑
- 将
route.params.id加入useEffect依赖数组,确保参数变化时重新请求 - 添加加载、错误状态处理:
export function StackTokenDetail({ route }) { const [token, setToken] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) useEffect(() => { const fetchTokenDetail = async () => { setLoading(true) try { const response = await fetch(`https://api.coingecko.com/api/v3/coins/${route.params.id}`) const json = await response.json() setToken(json) setError(null) } catch (e) { console.error(e) setError('加载代币详情失败') } finally { setLoading(false) } } fetchTokenDetail() }, [route.params.id]) if (loading) { return <SafeAreaView><Text>加载中...</Text></SafeAreaView> } if (error) { return <SafeAreaView><Text>{error}</Text></SafeAreaView> } return ( <SafeAreaView> <ScrollView style={{ flex: 1, padding: 40 }}> <TokenDetail token={token} /> </ScrollView> </SafeAreaView> ) }
4. 优化TokenList的初始值
将listToken初始值设为空数组,避免FlatList因data为null出现异常:
const [listToken, setListToken] = useState([])
内容的提问来源于stack exchange,提问作者user4016522
相关产品推荐
相关产品推荐

