React使用Context加载data.json数据访问属性报undefined求助
问题原因
- 核心问题是状态类型混乱+非法更新逻辑:你用同一个
planet状态同时存「全部星球数组」和「单个选中星球对象」两种完全不同类型的值,还在组件根作用域直接执行setState触发无限重渲染 - 具体错误逻辑:
- 初始渲染:
planet为undefined - 异步请求返回后,
setPlanets(transformedData)把planet设为星球数组,触发重渲染 - 重渲染时执行根作用域的
if (planet) { setPlanets(planet[0]) },把planet改为数组第一个元素(对象),再次触发重渲染 - 第二次重渲染时
planet是单个对象,执行planet[0]得到undefined,又把planet设为undefined - 循环往复导致状态值一直在数组、对象、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
相关产品推荐
相关产品推荐

