Home > Software design >  Pokeapi pokemonDetails.map is not a function & FIXED [Module '"react-router-dom"'
Pokeapi pokemonDetails.map is not a function & FIXED [Module '"react-router-dom"'

Time:12-08

I am working with the Pokeapi using React and TypeScript. While fetching data to show all Pokemon on one side, everything is working fine. But when clicking on a specific Pokemon to show their details, I get 2 errors.

UPDATE: FIXED! #1 Error: I cannot use {match} because Module '"react-router-dom"' has no exported member 'RouteComponentProps'. I used this in a ReactJS project a few months ago and everything worked fine.

#2 Error: When mapping throw each Pokemon, the error pokemonDetails.map is not a function appears, while working with map in the other Component (showing all Pokemon on one page), everything works fine.

Here is my App.js Component using Router:

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;

Here is my Pokedex Component (showing all Pokemon on one side):

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;

UPDATE: Here is the Pokemon Component (for showing details of each 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;

The API with the "id" should return this https://pokeapi.co/api/v2/pokemon/1

And here is my package.json file in case you need it:

  "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"
    ]
  }
}

CodePudding user response:

In react-router-dom v6 there are no longer route props (i.e. no history, location, or match props) like the Route components had in RRDv5. In v6 you should use the React hooks to access the matched route's match params.

  1. Use the Link component instead of a raw anchor (<a />) tag to link to the specific Pokemon page.

    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>
    ))}
    
  2. Use useParams hook to get the matched id match param from the path.

    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 (
        ...
      );
    };
    
  • Related