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

React项目CryptoDetails组件cryptoDetails未定义错误求助

问题:React组件渲染时触发“cryptoDetails is undefined”错误

在React项目中使用CryptoDetails.jsx组件时,渲染后出现cryptoDetails is undefined错误,组件无法正常渲染。已确认API及其他功能运行正常,错误源于代码中直接访问未定义的cryptoDetails属性。

错误原因分析

  1. 数据加载逻辑漏洞:仅通过isFetching判断加载状态,但RTK Query的isFetching仅在请求进行中为true。当请求完成但返回数据结构不符合预期(如data.data.coin不存在)或请求失败时,cryptoDetails会变为undefined,后续代码直接访问其属性就会触发错误。
  2. 未做属性访问兜底:代码中stats、genericStats及JSX部分直接访问cryptoDetails.price、cryptoDetails.rank等属性,未添加可选链或空值判断,一旦cryptoDetails未定义就会报错。
  3. 缺少错误状态处理:未捕获RTK Query请求失败的error状态,无法区分“加载中”和“请求失败”场景。

解决方法

1. 完善数据校验逻辑

在渲染核心内容前,依次判断加载状态、错误状态及cryptoDetails是否存在,避免未定义数据流入渲染环节:

if (isFetching) return "Loading..";
if (error) return <div>请求失败:{error.message}</div>;
if (!cryptoDetails) return <div>未找到对应加密货币数据</div>;

2. 对所有属性访问添加可选链

所有访问cryptoDetails属性的位置,都添加?.可选链操作符,同时对空值设置兜底显示(如N/A):

  • 示例:cryptoDetails?.price替代cryptoDetails.price
  • 嵌套属性需逐层添加:cryptoDetails?.allTimeHigh?.price

3. 优化列表渲染的空值处理

对于cryptoDetails.links这类数组,先判断长度再渲染,避免空数组或未定义数组触发map错误。

修改后的完整组件代码

import React, { useState } from "react";
import HTMLReactParser from "html-react-parser";
import { useParams } from "react-router-dom";
import millify from "millify";
import { Col, Row, Typography, Select } from "antd";
import {
  MoneyCollectOutlined,
  DollarCircleOutlined,
  FundOutlined,
  ExclamationCircleOutlined,
  StopOutlined,
  TrophyOutlined,
  CheckOutlined,
  NumberOutlined,
  ThunderboltOutlined,
} from "@ant-design/icons";

import {
  useGetCryptoDetailsQuery,
  useGetCryptoHistoryQuery,
} from "../services/cryptoApi";

import LineChart from "./LineChart";

const { Title, Text } = Typography;
const { Option } = Select;

const CryptoDetails = () => {
  const { coinId } = useParams();
  const [timeperiod, setTimeperiod] = useState("7d");
  const { data, isFetching, error } = useGetCryptoDetailsQuery(coinId);
  const { data: coinHistory } = useGetCryptoHistoryQuery({
    coinId,
    timeperiod,
  });
  const cryptoDetails = data?.data?.coin;

  if (isFetching) return "Loading..";
  if (error) return <div>请求失败:{error.message}</div>;
  if (!cryptoDetails) return <div>未找到对应加密货币数据</div>;

  const time = ["3h", "24h", "7d", "30d", "1y", "3m", "3y", "5y"];

  const stats = [
    {
      title: "Price to USD",
      value: `$ ${cryptoDetails?.price ? millify(cryptoDetails.price) : 'N/A'}`,
      icon: <DollarCircleOutlined />,
    },
    { title: "Rank", value: cryptoDetails?.rank || 'N/A', icon: <NumberOutlined /> },
    {
      title: "24h Volume",
      value: `$ ${cryptoDetails?.volume ? millify(cryptoDetails.volume) : 'N/A'}`,
      icon: <ThunderboltOutlined />,
    },
    {
      title: "Market Cap",
      value: `$ ${cryptoDetails?.marketCap ? millify(cryptoDetails.marketCap) : 'N/A'}`,
      icon: <DollarCircleOutlined />,
    },
    {
      title: "All-time-high(daily avg.)",
      value: `$ ${cryptoDetails?.allTimeHigh?.price ? millify(cryptoDetails.allTimeHigh.price) : 'N/A'}`,
      icon: <TrophyOutlined />,
    },
  ];

  const genericStats = [
    {
      title: "Number Of Markets",
      value: cryptoDetails?.numberOfMarkets || 'N/A',
      icon: <FundOutlined />,
    },
    {
      title: "Number Of Exchanges",
      value: cryptoDetails?.numberOfExchanges || 'N/A',
      icon: <MoneyCollectOutlined />,
    },
    {
      title: "Approved Supply",
      value: cryptoDetails?.approvedSupply ? (
        <CheckOutlined />
      ) : (
        <StopOutlined />
      ),
      icon: <ExclamationCircleOutlined />,
    },
    {
      title: "Total Supply",
      value: `$ ${cryptoDetails?.totalSupply ? millify(cryptoDetails.totalSupply) : 'N/A'}`,
      icon: <ExclamationCircleOutlined />,
    },
    {
      title: "Circulating Supply",
      value: `$ ${cryptoDetails?.circulatingSupply ? millify(cryptoDetails.circulatingSupply) : 'N/A'}`,
      icon: <ExclamationCircleOutlined />,
    },
  ];

  return (
    <Col className="coin-detail-container">
      <Col className="coin-heading-container">
        <Title level={2} className="coin-name">
          {cryptoDetails?.name} ({cryptoDetails?.slug}) Price
        </Title>
        <p>
          {cryptoDetails?.name} live price in US Dollar (USD). View value
          statistics, market cap and supply.
        </p>
      </Col>
      <Select
        defaultValue="7d"
        className="select-timeperiod"
        placeholder="Select Timeperiod"
        onChange={(value) => setTimeperiod(value)}
      >
        {time.map((date) => (
          <Option key={date}>{date}</Option>
        ))}
      </Select>
      <LineChart
        coinHistory={coinHistory}
        currentPrice={millify(cryptoDetails?.price || 0)}
        coinName={cryptoDetails?.name}
      />
      <Col className="stats-container">
        <Col className="coin-value-statistics">
          <Col className="coin-value-statistics-heading">
            <Title level={3} className="coin-details-heading">
              {cryptoDetails?.name} Value Statistics
            </Title>
            <p>
              An overview showing the statistics of {cryptoDetails?.name}, such
              as the base and quote currency, the rank, and trading volume.
            </p>
          </Col>
          {stats.map(({ icon, title, value }) => (
            <Col className="coin-stats" key={title}>
              <Col className="coin-stats-name">
                <Text>{icon}</Text>
                <Text>{title}</Text>
              </Col>
              <Text className="stats">{value}</Text>
            </Col>
          ))}
        </Col>
        <Col className="other-stats-info">
          <Col className="coin-value-statistics-heading">
            <Title level={3} className="coin-details-heading">
              Other Stats Info
            </Title>
            <p>
              An overview showing the statistics of {cryptoDetails?.name}, such
              as the base and quote currency, the rank, and trading volume.
            </p>
          </Col>
          {genericStats.map(({ icon, title, value }) => (
            <Col className="coin-stats" key={title}>
              <Col className="coin-stats-name">
                <Text>{icon}</Text>
                <Text>{title}</Text>
              </Col>
              <Text className="stats">{value}</Text>
            </Col>
          ))}
        </Col>
      </Col>
      <Col className="coin-desc-link">
        <Row className="coin-desc">
          <Title level={3} className="coin-details-heading">
            What is {cryptoDetails?.name}?
          </Title>
          {cryptoDetails?.description ? HTMLReactParser(cryptoDetails.description) : '暂无描述'}
        </Row>
        <Col className="coin-links">
          <Title level={3} className="coin-details-heading">
            {cryptoDetails?.name} Links
          </Title>
          {cryptoDetails?.links?.length ? (
            cryptoDetails.links.map((link) => (
              <Row className="coin-link" key={link.name}>
                <Title level={5} className="link-name">
                  {link.type}
                </Title>
                <a href={link.url} target="_blank" rel="noreferrer">
                  {link.name}
                </a>
              </Row>
            ))
          ) : (
            <Text>暂无相关链接</Text>
          )}
        </Col>
      </Col>
    </Col>
  );
};

export default CryptoDetails;

内容的提问来源于stack exchange,提问作者Pranav Kangale

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 11:57:10