ReactJS访问嵌套对象问题:无法获取CoinMarketCap API指定字段
我帮你梳理下几个关键问题,这些问题导致你无法正确获取total_market_cap和total_volume_24h:
1. 接口数据结构嵌套问题
CoinMarketCap v2的/global接口返回的数据里,total_market_cap和total_volume_24h并不是直接在根对象下,而是嵌套在quotes.USD层级里。接口返回的核心结构大概是这样:
{ "data": { "active_cryptocurrencies": xxx, "active_markets": xxx, "quotes": { "USD": { "total_market_cap": xxx, "total_volume_24h": xxx } } } }
所以你需要从quotes.USD里去取这两个值,而不是直接从根对象获取。
2. ComponentDidMount里的函数未执行
你在componentDidMount里只是写了this.props.fetchOverviewData,但没有调用这个函数,应该加上括号执行:this.props.fetch()(注意你的mapDispatchToProps里命名的是fetch,不是fetchOverviewData)。
3. mapDispatchToProps写法错误
当前你的mapDispatchToProps直接执行了dispatch(fetchMarketOverviewData()),这会导致组件初始化时就触发action,而且不是以函数的形式提供给props。正确的写法应该是返回一个包含函数的对象,让组件可以按需调用:
const mapDispatchToProps = dispatch => ({ fetch: () => dispatch(fetchMarketOverviewData()) });
4. getDerivedStateFromProps的判断逻辑不严谨
初始状态下overview是{}(你设置的defaultProps),不是undefined,所以nextProps.overview === undefined永远不会成立,导致isLoading一直为true(除非你的redux状态直接把overview设为undefined)。应该判断overview是否有有效数据:
static getDerivedStateFromProps(nextProps, prevState) { if (!nextProps.overview || !Object.keys(nextProps.overview).length) return null; return { isLoading: false }; }
5. Render里的Formatter未传入正确值
你现在的formatter.format()是空的,需要传入对应的USD市场数据,同时还要处理数据可能为undefined的情况,避免报错:
formatter.format(quotes?.USD?.total_market_cap || 0)
修正后的完整代码
import React, { Component } from "react"; import { render } from "react-dom"; import PropTypes from "prop-types"; import styled from "styled-components"; import { ScaleLoader } from "halogenium"; import styleConstants from "../misc/style_constants.js"; import { connect } from "react-redux"; import { fetchMarketOverviewData } from "../actions/api"; const Wrapper = styled.section` color: ${styleConstants.get("Light")}; margin: 20px 0; `; const Table = styled.table` width: 100%; `; const TableData = styled.td` &:nth-child(even) { text-align: right; } padding: 5px; border-bottom: #234558 solid 0.1px; `; const formatter = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", minimumFractionDigits: 0 }); export class Overview extends Component { state = { isLoading: true }; static propTypes = { overview: PropTypes.object, fetch: PropTypes.func.isRequired }; static defaultProps = { overview: {} }; componentDidMount() { // 调用fetch函数触发数据请求 this.props.fetch(); } static getDerivedStateFromProps(nextProps, prevState) { // 判断overview是否有有效数据 if (!nextProps.overview || Object.keys(nextProps.overview).length === 0) { return null; } return { isLoading: false }; } render() { if (this.state.isLoading) { return ( <ScaleLoader color={styleConstants.get("Light")} size="16px" margin="4px" /> ); } else { const { active_cryptocurrencies, active_markets, quotes } = this.props.overview; // 解构USD数据,处理可能的undefined const { total_market_cap = 0, total_volume_24h = 0 } = quotes?.USD || {}; return ( <Wrapper> <Table> <tbody> <tr> <TableData>Total Market Cap</TableData> <TableData>{formatter.format(total_market_cap)}</TableData> </tr> <tr> <TableData>Total 24h Volume</TableData> <TableData>{formatter.format(total_volume_24h)}</TableData> </tr> <tr> <TableData>Active Markets</TableData> <TableData>{active_markets}</TableData> </tr> <tr> <TableData>Active Currencies</TableData> <TableData>{active_cryptocurrencies}</TableData> </tr> </tbody> </Table> </Wrapper> ); } } } const mapStateToProps = state => { const { data } = state.api.marketOverviewData; return { overview: data || {} }; // 确保overview永远是对象,避免undefined }; const mapDispatchToProps = dispatch => ({ fetch: () => dispatch(fetchMarketOverviewData()) }); export default connect(mapStateToProps, mapDispatchToProps)(Overview);
另外,建议你在redux的reducer里处理数据时,确保marketOverviewData.data初始值是{},这样可以避免props.overview变成undefined的情况。
内容的提问来源于stack exchange,提问作者Paul McLoughlin

