You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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属性的代码并刷新,重新添加代码后再刷新,可正常获取并显示数据,但返回列表页再次点击任意代币时又会崩溃。

原因分析

  1. 初始值为空导致渲染错误:StackTokenDetail中token初始值为null,但TokenDetail直接访问token.name、token.symbol等属性,组件挂载时API请求未完成,此时token为空,触发属性读取错误。
  2. 导航传参格式错误:ItemToken跳转时传递的id是数组格式[item.id],但StackTokenDetail直接用该值拼接API地址,导致请求地址异常(如coins/[bitcoin]),返回数据结构不符合预期。
  3. 无加载/错误状态处理:组件未在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 23:08:08