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

React使用Context加载data.json数据访问属性报undefined求助

问题原因
  • 核心问题是状态类型混乱+非法更新逻辑:你用同一个planet状态同时存「全部星球数组」和「单个选中星球对象」两种完全不同类型的值,还在组件根作用域直接执行setState触发无限重渲染
  • 具体错误逻辑:
    1. 初始渲染:planet为undefined
    2. 异步请求返回后,setPlanets(transformedData) 把planet设为星球数组,触发重渲染
    3. 重渲染时执行根作用域的if (planet) { setPlanets(planet[0]) },把planet改为数组第一个元素(对象),再次触发重渲染
    4. 第二次重渲染时planet是单个对象,执行planet[0]得到undefined,又把planet设为undefined
    5. 循环往复导致状态值一直在数组、对象、undefined之间跳,自然读不到稳定的属性值
  • 补充说明:控制台打印引用类型是惰性求值,你看到的数组是展开时的最新值,不是代码执行到打印语句那一刻的值,所以会有「打印数组正常但读属性报错」的错觉
解决方法

把「全部星球列表」和「当前选中星球」拆分成两个独立状态,移除根作用域的非法更新逻辑即可:

修改ContextProvider代码

import React from "react";
import { useState, useEffect } from "react";

export const Context = React.createContext({
  onChooseMoon: () => {},
  onChooseMars: () => {},
  currentPlanet: null,
});

const ContextProvider = (props) => {
  // 存所有星球的数组,固定类型为数组
  const [planets, setPlanets] = useState([]);
  // 存当前选中的单个星球,固定类型为对象/undefined
  const [currentPlanet, setCurrentPlanet] = useState(null);

  useEffect(() => {
    fetch("./data.json")
      .then((response) => response.json())
      .then((data) => {
        const transformedData = data.destinations.map((destination) => {
          return {
            name: destination.name,
            img: destination.images.png,
            desc: destination.description,
            dist: destination.distance,
            travel: destination.travel,
          };
        });
        setPlanets(transformedData);
        // 首次加载默认选中第一个星球
        setCurrentPlanet(transformedData[0]);
      });
  }, []);

  const setMoonHandler = () => {
    setCurrentPlanet(planets[0]);
  };

  const setMarsHandler = () => {
    setCurrentPlanet(planets[1]);
  };

  return (
    <Context.Provider
      value={{
        currentPlanet: currentPlanet,
        onChooseMoon: setMoonHandler,
        onChooseMars: setMarsHandler,
      }}
    >
      {props.children}
    </Context.Provider>
  );
};

export default ContextProvider;

修改Destination组件代码

import Header from "../components/Header/Header";
import Planets from "../components/Planets/Planets";
import styles from "./Destination.module.css";
import ChoosePlanet from "../components/Planets/ChoosePlanet";
import { useContext } from "react";
import { Context } from "../store/context";

const Destination = () => {
  const ctx = useContext(Context);

  if (ctx.currentPlanet) {
    // 直接读单个对象的属性即可
    console.log(ctx.currentPlanet.name);
    console.log(ctx.currentPlanet);
  }

  return (
    <div className={styles.body}>
      <Header />
      <h2>
        <span>01</span>PICK YOUR DESTINATION
      </h2>
      <main className={styles.main}>
        {ctx.currentPlanet && <Planets img={ctx.currentPlanet.img} alt="planet" />}
        {ctx.currentPlanet && (
          <ChoosePlanet
            name={ctx.currentPlanet.name}
            text={ctx.currentPlanet.desc}
            distance={ctx.currentPlanet.dist}
            time={ctx.currentPlanet.travel}
          />
        )}
      </main>
    </div>
  );
};

export default Destination;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 13:45:03