React使用useState存储过滤后的数组传入Modal时提示变量未定义
问题描述
我为即将到来的NFL赛季制作了一个JSON文件,当前组件已经实现了可正常运行的fetch方法获取数据,存储数据的变量命名为squads。现在需要实现点击按钮过滤出选中球队的赛程,并将其展示在Modal中,本示例中的按钮为硬编码实现。Modal组件本身运行正常,且Modal主体部分预留了{props.children}用于接收传入的数据。
我尝试使用useState将过滤得到的球队数据赋值给selectedTeam变量,运行后收到了变量未定义的错误提示,代码如下:
import React, { useState, useEffect } from "react"; import Modal from "./Components/Modal"; export default function App() { const [show, setShow] = useState(false); const [title, setTitle] = useState(""); const [squads, setSquads] = useState([]); const [modalTitleBackground, setModalTitleBackground] = useState(""); const [image, setImage] = useState(""); const [selectedTeam, setSelectedTeam] = useState([]); const url = "../nfl2021.json"; const fetchData = async () => { try { const response = await fetch(url); const data = await response.json(); setSquads(data.teams); } catch (error) { console.log(error); } }; useEffect(() => { fetchData(); }, []); const filterTeam = (team) => { setSelectedTeam(squads.filter((squad) => squad.name === team)); console.log(selectedTeam); setTitle(selectedTeam[0].name); setModalTitleBackground(selectedTeam[0].topBG); setImage(`./images/${selectedTeam[0].img}`); setShow(true); }; return ( <div className="App"> <button onClick={() => filterTeam("New England Patriots")}> Show Modal </button> <button onClick={() => filterTeam("Green Bay Packers")}> Show Modal 2 </button> <button onClick={() => filterTeam("Cincinnati Bengals")}> Show Modal 3 </button> <Modal image={image} title={title} backgroundColor={modalTitleBackground} onClose={() => setShow(false)} show={show} > <p> This is the modal body using props.children in the modal component. </p> <p>The {title} 2021 schedule.</p> {selectedTeam[0].schedule.map((schedule, index) => { return ( <p> Week {index + 1}: The {selectedTeam[0].nickname} play the{" "} {selectedTeam[0].schedule[index].opponent}. </p> ); })} </Modal> </div> ); }
错误原因
React的setState是异步更新的,调用setSelectedTeam后,当前函数作用域内的selectedTeam还是更新前的初始空数组,取selectedTeam[0]会得到undefined,直接访问属性就会触发报错。同时初始渲染时selectedTeam是空数组,Modal内直接访问selectedTeam[0]也会报错。
修复方案
- 将filter筛选后的结果存到临时变量,后续更新其他状态直接使用该临时变量,不读取刚更新的state
- 将
selectedTeam初始值改为null,渲染赛程时加条件判断,避免空值报错 - 优化赛程渲染逻辑,直接使用map方法返回的赛程对象,无需重复取下标
修正后代码
import React, { useState, useEffect } from "react"; import Modal from "./Components/Modal"; export default function App() { const [show, setShow] = useState(false); const [title, setTitle] = useState(""); const [squads, setSquads] = useState([]); const [modalTitleBackground, setModalTitleBackground] = useState(""); const [image, setImage] = useState(""); // 初始值改为null,方便判断是否有选中球队 const [selectedTeam, setSelectedTeam] = useState(null); const url = "../nfl2021.json"; const fetchData = async () => { try { const response = await fetch(url); const data = await response.json(); setSquads(data.teams); } catch (error) { console.log(error); } }; useEffect(() => { fetchData(); }, []); const filterTeam = (team) => { // 筛选结果存到临时变量 const matchedTeam = squads.filter(squad => squad.name === team)[0]; setSelectedTeam(matchedTeam); // 直接用临时变量更新其他状态 setTitle(matchedTeam.name); setModalTitleBackground(matchedTeam.topBG); setImage(`./images/${matchedTeam.img}`); setShow(true); }; return ( <div className="App"> <button onClick={() => filterTeam("New England Patriots")}> Show Modal </button> <button onClick={() => filterTeam("Green Bay Packers")}> Show Modal 2 </button> <button onClick={() => filterTeam("Cincinnati Bengals")}> Show Modal 3 </button> <Modal image={image} title={title} backgroundColor={modalTitleBackground} onClose={() => setShow(false)} show={show} > <p> This is the modal body using props.children in the modal component. </p> <p>The {title} 2021 schedule.</p> {/* 加条件判断,有选中球队才渲染赛程 */} {selectedTeam && selectedTeam.schedule.map((schedule, index) => ( <p key={index}> Week {index + 1}: The {selectedTeam.nickname} play the {schedule.opponent}. </p> ))} </Modal> </div> ); }
内容的提问来源于stack exchange,提问作者John G.
相关产品推荐
相关产品推荐

