ReactJS中getResult无法调用map函数问题求助
问题:渲染时getResult无法调用map函数
我在为API编写前端时遇到了问题:渲染时getResult无法调用map函数。已经把初始状态从null改为[],但问题依然存在。以下是Filter、GameTable和Game组件的代码,求解决思路。
组件代码
Filter组件
import React, { useState, useEffect } from 'react' import { useQuery } from 'react-query'; import gameService from '../services/gameService' import GameTable from './GameTable' import '../App.css' const Filter = () => { const [getName, setGetName] = useState("") const [getGenre, setGetGenre] = useState("") //const [getPublisher, setGetPublisher] = useState("") const [metadata,setMetadata] = useState([]) const [getResult, setGetResult] = useState([]) const fortmatResponse = (res) => { return JSON.stringify(res,null, 2); } const {isLoading: isLoadingGames, refetch: getAllGames} = useQuery("query-games", async() => { return await gameService.get("/games"); }, { enabled: false, onSuccess: (res) => { const result = { status : res.status + "-" + res.statusText, headers: res.headers, data: res.data, }; setMetadata(fortmatResponse(result.data.metadata)); setGetResult(fortmatResponse(result.data.games)); }, onError : (err) => { setGetResult(fortmatResponse(err.response?.data || err)); }, } ); useEffect(() => { if (isLoadingGames) setGetResult("loading..."); },[isLoadingGames]); function getAllData() { try{ getAllGames(); }catch(err) { setGetResult(fortmatResponse(err)); } } const {isLoading: isLoadingGame, refetch: getGamesByName} = useQuery( "query-games-by-name", async () => { return await gameService.get(`/games?name=${getName}`); }, { enabled: false, onSuccess: (res) => { const result = { status : res.status + "-" + res.statusText, headers: res.headers, data: res.data, }; setMetadata(fortmatResponse(result.data.metadata)); setGetResult(fortmatResponse(result.data.games)); }, onError : (err) => { setGetResult(fortmatResponse(err.response?.data || err)); }, } ); useEffect(() => { if (isLoadingGame) setGetResult("loading..."); }, [isLoadingGame]); function getDataByName() { if (getName) { try { getGamesByName(); } catch (err) { setGetResult(fortmatResponse(err)); } } } const {isLoading: isSearchingGame, refetch: findGamesByGenre} = useQuery( "query-games-by-genre", async () => { return await gameService.get(`/games?genre=${getGenre}`); }, { enabled: false, onSuccess: (res) => { const result = { status : res.status + "-" + res.statusText, headers: res.headers, data: res.data, }; setMetadata(fortmatResponse(result.data.metadata)); setGetResult(fortmatResponse(result.data.games)); }, onError : (err) => { setGetResult(fortmatResponse(err.response?.data || err)); }, } ); useEffect(() => { if (isSearchingGame) setGetResult("loading..."); }, [isSearchingGame]); function getDataByGenre() { if (getGenre) { try { findGamesByGenre(); } catch (err) { setGetResult(fortmatResponse(err)); } } } const clearGetOutput =() => { setGetResult([]); } console.log(metadata) return( <div className='card'> <div className='card-header input-group-sm'> GET Request </div> <div className='card-body'> <div className='input-group input-group-sm'> <button className='btn btn-sm btn-primary' onClick={getAllData}> Get All </button> <input type="text" value={getName} onChange={(e) => setGetName(e.target.value)} className='form-control ml-2' placeholder='Name' /> <div className='input-group-append'> <button className="btn btn-sm btn-primary" onClick={getDataByName}> Get by Name </button> </div> <input type="text" value={getGenre} onChange={(e) => setGetGenre(e.target.value)} className="form-control ml-2" placeholder="Genre" /> <div className="input-group-append"> <button className="btn btn-sm btn-primary" onClick={getDataByGenre}> Find By Genre </button> </div> <button className="btn btn-sm btn-warning ml-2" onClick={clearGetOutput}> Clear </button> </div> <GameTable games={getResult} /> </div> </div> ) } export default Filter;
GameTable组件
import React from 'react' import Game from './Game' import '../App.css' const GameTable = ({games}) => { return ( <div className="table-wrapper"> <h2>Games</h2> <table className='fl-table'> <thead> <tr> <th> Name </th> <th> Genre </th> <th> Publisher </th> </tr> </thead> <tbody> { games.map(game => <Game key={game.id} game={game} /> ) } </tbody> </table> </div> ) } export default GameTable;
Game组件
import React from 'react' const Game = ({game}) => { return ( <tr> <td>{game.name}</td> <td>{game.genre}</td> <td>{game.publisher_name}</td> </tr> ) } export default Game
解决思路
核心问题
你用fortmatResponse函数把API返回的游戏数组转成了JSON字符串,而字符串类型没有map方法,导致GameTable组件调用games.map时报错。同时你还把getResult设置过"loading..."字符串,进一步混淆了数据类型。
具体修改步骤
直接保存原始数组,不要转成字符串
在所有useQuery的onSuccess回调里,去掉fortmatResponse对游戏数组的处理,直接赋值原始数组:// 原代码 setGetResult(fortmatResponse(result.data.games)); // 修改后 setGetResult(result.data.games);分离loading状态与数据状态
不要把getResult设置为"loading...",而是利用react-query提供的isLoading状态单独处理加载提示:- 在Filter组件中把所有loading状态传给GameTable:
<GameTable games={getResult} isLoading={isLoadingGames || isLoadingGame || isSearchingGame} /> - 修改GameTable组件,根据loading状态显示对应内容:
const GameTable = ({games, isLoading}) => { if (isLoading) return <div>loading...</div>; // 如果数组为空,也可以加个空状态提示 if (!games.length) return <div>暂无游戏数据</div>; return ( <div className="table-wrapper"> <h2>Games</h2> <table className='fl-table'> {/* 原有表格代码 */} </table> </div> ) }
- 在Filter组件中把所有loading状态传给GameTable:
分离错误状态
不要把错误信息存到getResult里,单独用一个状态管理错误:// 在Filter组件顶部新增状态 const [error, setError] = useState(null); // 在所有useQuery的onError回调里修改 setError(err.response?.data || err); // 在Filter组件的return里添加错误提示 {error && <div style={{color: 'red'}}>{JSON.stringify(error)}</div>}清理无用代码
删除之前用来设置loading文本的useEffect,因为已经通过react-query的isLoading状态处理加载逻辑。
内容的提问来源于stack exchange,提问作者Can Yolal
相关产品推荐
相关产品推荐

