Pokeapi pokemonDetails.map 不是函数且已修复 [模块“"react-router-dom"”没有导出成员 'RouteComponentProps']
Pokeapi pokemonDetails.map is not a function & FIXED [Module '"react-router-dom"' has no exported member 'RouteComponentProps' ]
我正在使用 React 和 TypeScript 处理 Pokeapi。在获取数据以在一侧显示所有神奇宝贝时,一切正常。但是当点击一个特定的口袋妖怪来显示他们的详细信息时,我得到 2 个错误。
更新:已修复! #1 错误:我无法使用 {match} 因为模块“react-router-dom”'没有导出成员 'RouteComponentProps'。几个月前,我在一个 ReactJS 项目中使用了它,一切正常。
#2 错误:当 mapping 抛出每个 Pokemon 时,出现错误 pokemonDetails.map is not a function,而在另一个组件中使用 map(在一页上显示所有 Pokemon)时,一切正常。
这是我的 App.js 组件 使用路由器:
import Pokedex from './Pokedex';
import Pokemon from './Pokemon';
import { Route, Routes } from 'react-router-dom';
function App() {
return (
<Routes>
<Route path='/' element={<Pokedex />} />
<Route path='/pokemon/:id' element={<Pokemon />} />
</Routes>
);
}
export default App;
这是我的 Pokedex 组件(在一侧显示所有神奇宝贝):
import { useEffect, useState } from 'react';
import Container from 'react-bootstrap/Container';
import Col from 'react-bootstrap/Col';
import Row from 'react-bootstrap/Row';
const Pokedex = () => {
const [pokemon, setPokemon] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
getPokedex();
}, []);
const getPokedex = async () => {
try {
const res = await fetch('https://pokeapi.co/api/v2/pokemon?limit=151');
const { results } = await res.json();
const pokedex = results.map((pokemon: any, index: number) => {
const paddedId = ('00' + (index + 1)).slice(-3);
const image = `https://assets.pokemon.com/assets/cms2/img/pokedex/detail/${paddedId}.png`;
return { ...pokemon, image };
});
setPokemon(pokedex);
setLoading(false);
} catch (err) {
console.error(err);
}
};
return (
<Container>
{loading ? (
'Fetching Pokemon...'
) : (
<Row>
{pokemon.map((pokemon: any, index: number) => (
<Col key={index} xs={1} sm={1} md={1} lg={1} xl={1}>
<a href={`/pokemon/${index + 1}`}>
<img src={pokemon.image} alt={pokemon.name} />
<p>
{index + 1}.<span>{pokemon.name}</span>
</p>
</a>
</Col>
))}
</Row>
)}
</Container>
);
};
export default Pokedex;
更新:这是 Pokemon 组件(用于显示每个 Pokemon 的详细信息):
import { useEffect, useState } from 'react';
import Container from 'react-bootstrap/Container';
import { useParams } from 'react-router-dom';
const Pokemon = () => {
const [pokemonDetails, setPokemonDetails] = useState();
const [loading, setLoading] = useState(true);
const { id } = useParams();
useEffect(() => {
getPokemon(id);
}, [id]);
const getPokemon = async (id: string | undefined) => {
try {
const res = await fetch(`https://pokeapi.co/api/v2/pokemon/${id}`);
const pokemon = await res.json();
const paddedId = ('00' + id).slice(-3);
pokemon.image = `https://assets.pokemon.com/assets/cms2/img/pokedex/detail/${paddedId}.png`;
setPokemonDetails(pokemon);
setLoading(false);
} catch (err) {
console.error(err);
}
};
return (
<Container>
<h1>Pokemon</h1>
{loading ? (
'Fetching Pokemon...'
) : (
<div>
<h2>{pokemonDetails.name}</h2>
<h2>{pokemonDetails.id}</h2>
<img src={pokemonDetails.image} alt={pokemon.Detailsname} />
<p>
{pokemonDetails.types.map((type: any, index: number) => (
<p key={index}>{type.type.name}</p>
))}
</p>
</div>
)}
</Container>
);
};
export default Pokemon;
带有“id”的API应该return这个
https://pokeapi.co/api/v2/pokemon/1
这是我的package.json文件,以备不时之需:
"name": "pokemon",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.16.0",
"@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^12.8.3",
"@types/jest": "^27.0.3",
"@types/node": "^16.11.11",
"@types/react": "^17.0.37",
"@types/react-dom": "^17.0.11",
"@types/react-router-dom": "^5.3.2",
"axios": "^0.24.0",
"bootstrap": "^5.1.3",
"react": "^17.0.2",
"react-bootstrap": "^2.0.3",
"react-dom": "^17.0.2",
"react-router-dom": "^6.0.2",
"react-scripts": "4.0.3",
"typescript": "^4.5.2",
"web-vitals": "^1.1.2"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
在 react-router-dom
v6 中不再有 route props(即没有 history
、location
或 match
props) 就像 RRDv5 中的 Route
组件一样。在 v6 中,您应该使用 React 钩子来访问匹配路由的匹配参数。
使用 Link
组件而不是原始锚点 (<a />
) 标签 link 到特定的 Pokemon 页面。
import { Link } from 'react-router-dom';
...
{pokemon.map((pokemon: any, index: number) => (
<Col key={index} xs={1} sm={1} md={1} lg={1} xl={1}>
<Link to={`/pokemon/${index + 1}`}>
<img src={pokemon.image} alt={pokemon.name} />
<p>
{index + 1}.<span>{pokemon.name}</span>
</p>
</Link>
</Col>
))}
使用 useParams
钩子从路径中获取匹配的 id
匹配参数。
import { useParams } from 'react-router-dom';
const Pokemon = () => {
const [pokemonDetails, setPokemonDetails] = useState([]);
const [loading, setLoading] = useState(true);
const { id } = useParams();
useEffect(() => {
getPokemon(id);
}, [id]);
const getPokemon = async (id: number) => {
try {
const res = await fetch(`https://pokeapi.co/api/v2/pokemon/${id}`);
const pokemon = await res.json();
const paddedId = ('00' + id).slice(-3);
pokemon.image = `https://assets.pokemon.com/assets/cms2/img/pokedex/detail/${paddedId}.png`;
setPokemonDetails(pokemon);
setLoading(false);
} catch (err) {
console.error(err);
}
};
return (
...
);
};
至于打字稿,我必须将状态类型声明为 any,现在可以抓取了。
const [pokemonDetails, setPokemonDetails] = useState<any>([]);
我正在使用 React 和 TypeScript 处理 Pokeapi。在获取数据以在一侧显示所有神奇宝贝时,一切正常。但是当点击一个特定的口袋妖怪来显示他们的详细信息时,我得到 2 个错误。
更新:已修复! #1 错误:我无法使用 {match} 因为模块“react-router-dom”'没有导出成员 'RouteComponentProps'。几个月前,我在一个 ReactJS 项目中使用了它,一切正常。
#2 错误:当 mapping 抛出每个 Pokemon 时,出现错误 pokemonDetails.map is not a function,而在另一个组件中使用 map(在一页上显示所有 Pokemon)时,一切正常。
这是我的 App.js 组件 使用路由器:
import Pokedex from './Pokedex';
import Pokemon from './Pokemon';
import { Route, Routes } from 'react-router-dom';
function App() {
return (
<Routes>
<Route path='/' element={<Pokedex />} />
<Route path='/pokemon/:id' element={<Pokemon />} />
</Routes>
);
}
export default App;
这是我的 Pokedex 组件(在一侧显示所有神奇宝贝):
import { useEffect, useState } from 'react';
import Container from 'react-bootstrap/Container';
import Col from 'react-bootstrap/Col';
import Row from 'react-bootstrap/Row';
const Pokedex = () => {
const [pokemon, setPokemon] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
getPokedex();
}, []);
const getPokedex = async () => {
try {
const res = await fetch('https://pokeapi.co/api/v2/pokemon?limit=151');
const { results } = await res.json();
const pokedex = results.map((pokemon: any, index: number) => {
const paddedId = ('00' + (index + 1)).slice(-3);
const image = `https://assets.pokemon.com/assets/cms2/img/pokedex/detail/${paddedId}.png`;
return { ...pokemon, image };
});
setPokemon(pokedex);
setLoading(false);
} catch (err) {
console.error(err);
}
};
return (
<Container>
{loading ? (
'Fetching Pokemon...'
) : (
<Row>
{pokemon.map((pokemon: any, index: number) => (
<Col key={index} xs={1} sm={1} md={1} lg={1} xl={1}>
<a href={`/pokemon/${index + 1}`}>
<img src={pokemon.image} alt={pokemon.name} />
<p>
{index + 1}.<span>{pokemon.name}</span>
</p>
</a>
</Col>
))}
</Row>
)}
</Container>
);
};
export default Pokedex;
更新:这是 Pokemon 组件(用于显示每个 Pokemon 的详细信息):
import { useEffect, useState } from 'react';
import Container from 'react-bootstrap/Container';
import { useParams } from 'react-router-dom';
const Pokemon = () => {
const [pokemonDetails, setPokemonDetails] = useState();
const [loading, setLoading] = useState(true);
const { id } = useParams();
useEffect(() => {
getPokemon(id);
}, [id]);
const getPokemon = async (id: string | undefined) => {
try {
const res = await fetch(`https://pokeapi.co/api/v2/pokemon/${id}`);
const pokemon = await res.json();
const paddedId = ('00' + id).slice(-3);
pokemon.image = `https://assets.pokemon.com/assets/cms2/img/pokedex/detail/${paddedId}.png`;
setPokemonDetails(pokemon);
setLoading(false);
} catch (err) {
console.error(err);
}
};
return (
<Container>
<h1>Pokemon</h1>
{loading ? (
'Fetching Pokemon...'
) : (
<div>
<h2>{pokemonDetails.name}</h2>
<h2>{pokemonDetails.id}</h2>
<img src={pokemonDetails.image} alt={pokemon.Detailsname} />
<p>
{pokemonDetails.types.map((type: any, index: number) => (
<p key={index}>{type.type.name}</p>
))}
</p>
</div>
)}
</Container>
);
};
export default Pokemon;
带有“id”的API应该return这个 https://pokeapi.co/api/v2/pokemon/1
这是我的package.json文件,以备不时之需:
"name": "pokemon",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.16.0",
"@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^12.8.3",
"@types/jest": "^27.0.3",
"@types/node": "^16.11.11",
"@types/react": "^17.0.37",
"@types/react-dom": "^17.0.11",
"@types/react-router-dom": "^5.3.2",
"axios": "^0.24.0",
"bootstrap": "^5.1.3",
"react": "^17.0.2",
"react-bootstrap": "^2.0.3",
"react-dom": "^17.0.2",
"react-router-dom": "^6.0.2",
"react-scripts": "4.0.3",
"typescript": "^4.5.2",
"web-vitals": "^1.1.2"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
在 react-router-dom
v6 中不再有 route props(即没有 history
、location
或 match
props) 就像 RRDv5 中的 Route
组件一样。在 v6 中,您应该使用 React 钩子来访问匹配路由的匹配参数。
使用
Link
组件而不是原始锚点 (<a />
) 标签 link 到特定的 Pokemon 页面。import { Link } from 'react-router-dom'; ... {pokemon.map((pokemon: any, index: number) => ( <Col key={index} xs={1} sm={1} md={1} lg={1} xl={1}> <Link to={`/pokemon/${index + 1}`}> <img src={pokemon.image} alt={pokemon.name} /> <p> {index + 1}.<span>{pokemon.name}</span> </p> </Link> </Col> ))}
使用
useParams
钩子从路径中获取匹配的id
匹配参数。import { useParams } from 'react-router-dom'; const Pokemon = () => { const [pokemonDetails, setPokemonDetails] = useState([]); const [loading, setLoading] = useState(true); const { id } = useParams(); useEffect(() => { getPokemon(id); }, [id]); const getPokemon = async (id: number) => { try { const res = await fetch(`https://pokeapi.co/api/v2/pokemon/${id}`); const pokemon = await res.json(); const paddedId = ('00' + id).slice(-3); pokemon.image = `https://assets.pokemon.com/assets/cms2/img/pokedex/detail/${paddedId}.png`; setPokemonDetails(pokemon); setLoading(false); } catch (err) { console.error(err); } }; return ( ... ); };
至于打字稿,我必须将状态类型声明为 any,现在可以抓取了。
const [pokemonDetails, setPokemonDetails] = useState<any>([]);