React Context的planet状态变undefined排查及state更新写法疑问
问题原因分析
你遇到的planet最终变为undefined的问题,完全是顶层无包裹的if (planet)逻辑导致的,执行流程如下:
- 组件首次渲染:
planet初始值为undefined,不触发if逻辑,useEffect发起数据请求 - 数据请求完成后调用
setPlanets(transformedData),将planet赋值为星球数组,触发组件重渲染 - 本次重渲染时
planet是数组(真值),进入if逻辑,调用setPlanets(planet[0])将planet赋值为数组第一项的星球对象,再次触发重渲染 - 第三次重渲染时,
planet已经是单个星球对象,而非数组,此时访问planet[0]得到的就是undefined,调用setPlanets(undefined)后planet就被设为undefined,后续渲染不会再进入if逻辑,最终状态停留在undefined
相关问题解答
setPlanets(planet[0])这种直接使用当前state值更新state的写法本身语法没有问题,但有两个明显缺陷:
- 存在闭包陷阱:如果更新前state已经被其他逻辑修改,当前渲染作用域下拿到的
planet可能是旧值,导致更新不符合预期,更稳妥的写法是使用函数式更新:setPlanets(prev => prev[0]),参数prev永远是最新的state值 - 你的场景下这种写法本身逻辑错误:你用同一个state既存整个星球数组,又存单个选中的星球,逻辑混淆才会触发前面的bug
修复方案
拆分两个独立state分别存储全量星球数据和当前选中星球,避免逻辑冲突:
import React from "react"; import { useState, useEffect } from "react"; export const Context = React.createContext({ onChooseMoon: () => {}, onChooseMars: () => {}, }); const ContextProvider = (props) => { // 存储全量星球数组 const [planets, setPlanets] = useState([]); // 存储当前选中的星球 const [currentPlanet, setCurrentPlanet] = useState(); useEffect(() => { fetch("./data.json") .then((response) => { return 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={{ planet: currentPlanet, onChooseMoon: setMoonHandler, onChooseMars: setMarsHandler, }} > {props.children} </Context.Provider> ); }; export default ContextProvider;
内容的提问来源于stack exchange,提问作者Adrian
相关产品推荐
相关产品推荐

