0

我正在学习 React,因为我正在从 Pokéapi 获取数据以制作列表组件、卡片组件、详细信息组件和过滤器组件。我正在尝试制作一个过滤器,以便您可以按神奇宝贝类型进行过滤。只有还包含该类型字符串的卡片才会呈现(还没有)。所以我不确定a)我是否应该根据所选值从PokemonList中的API进行不同的调用,或者b)我是否应该比较这些值并根据比较更改PokemonCard元素在PokemonList.js中的呈现方式。我设法将数据从过滤器传递到列表组件。然后我一直在尝试将类型数据从 PokemonCard.js 传递到列表组件,以便我可以比较这两个值,但我发现很难使用回调从卡片组件传递类型数据,

在此处输入图像描述

我应该在这里使用哪种方法来简化过滤?有条件地进行不同的 API 调用或渲染 PokemonCard 元素?将过滤器选项与 PokemonList.js 中的口袋妖怪卡片类型进行比较是个好主意吗?那么我如何从卡片组件传递该数据,因为我没有通过点击事件传递它?

感谢您的任何想法!我从包含卡片、卡片组件和过滤器组件的列表组件粘贴代码。

PokemonList 组件:

import { useState } from 'react';
import useSWR from 'swr';
import PokemonCard from './PokemonCard';
import PokemonFilter from './PokemonFilter';
import './PokemonList.css';

const PokemonList = () => {

const [index, setIndex] = useState(0);
const [type, setType] = useState('');

function selectedType(type) { // value from filter dropdown
    setType(type)
    console.log("handled")
    console.log(type)
}

const url = `https://pokeapi.co/api/v2/pokemon?limit=9&offset=${index}`;

const fetcher = (...args) => fetch(...args).then((res) => res.json())
const { data: result, error } = useSWR(url, fetcher);

if (error) return <div>failed to load</div>
if (!result) return <div>loading...</div>

result.results.sort((a, b) => a.name < b.name ? -1 : 1);


return (
    <section>
        <PokemonFilter onSelectedType={selectedType} selectedPokemonType={type} />
        <div className="pokemon-list">
            <div className="pokemons">
                {result.results.map((pokemon) => (
                    <PokemonCard key={pokemon.name} pokemon={pokemon} /> // callback needed??
                ))}
            </div>
            <div className="pagination">
                <button 
                    onClick={() => setIndex(index - 9)} 
                    disabled={result.previous === null}
                >
                Previous
                </button>
                <button 
                    onClick={() => setIndex(index + 9)}
                    disabled={result.next === null}
                >
                Next
                </button>
            </div>
        </div>
    </section>
  )
}

export default PokemonList;

口袋妖怪卡组件:

import { Link } from "react-router-dom";
import useSWR from 'swr';
import './PokemonCard.css';

const PokemonCard = ({ pokemon }) => {

const { name } = pokemon;

const url = `https://pokeapi.co/api/v2/pokemon/${name}`;
const { data, error } = useSWR(url);

if (error) return <div>failed to load</div>
if (!data) return <div>loading...</div>

const { types, abilities } = data;

// types[0].type.name <---- value I want to pass to PokemonList.js

return (
        <div className='pokemon-card'>
            <div className='pokemon-card__content'>
                <img
                    className='pokemon-card__image'
                    src={data.sprites.front_default}
                    alt={name}
                />
                <div className='pokemon-card__info'>  
                <p className='pokemon-card__name'>Name: {name}</p>
                <p className='pokemon-card__abilities'>Abilities: {abilities[0].ability.name}</p>
                <p className='pokemon-card__categories'>Category: {types[0].type.name}</p> 
                </div>
            </div>
            <Link className='pokemon-card__link' to={{
                pathname: `/${name}`,
                state: data
                }}>
                View Details
            </Link>
        </div>
  )
}

export default PokemonCard;

PokemonFilter 组件:

import './PokemonFilter.css';
import useSWR from 'swr';

const PokemonFilter = ({onSelectedType, selectedPokemonType}) => {

const url = `https://pokeapi.co/api/v2/type/`;

const fetcher = (...args) => fetch(...args).then((res) => res.json())
const { data: result, error } = useSWR(url, fetcher);

if (error) return <div>failed to load</div>
if (!result) return <div>loading...</div>

function filteredTypeHandler(e) {
    console.log(e.target.value);
    onSelectedType(e.target.value);
}

console.log(selectedPokemonType)

return(
    <div className="pokemon-types__sidebar">
        <h2>Filter Pokémon by type</h2>
        <select 
            name="pokemon-type" 
            className="pokemon-types__filter"
            onChange={filteredTypeHandler}
            >
            <option value="All">Filter By Type</option>
            {result.results.map((type) => {
            return (
                <option key={type.name} value={type.name}> {type.name}</option>
                )
            })}
        </select>
    </div>
  )
}

export default PokemonFilter;
4

1 回答 1

1

这是一个改进,修改的示例……我没有测试,这只是一个视觉示例。

我不知道useSWR对不起,我axios在我的例子中使用......

如果你想集中你所有的 API 请求,你可以创建一个useApi钩子,在互联网上你会找到教程。

口袋妖怪列表.js

import React, { useState, useEffect } from 'react';
import axios from 'axios';                           // or swr

import PokemonFilter from './PokemonFilter';
import PokemonCard from './PokemonCard';

export default function PokemonList() {
  const [data, setData] = useState([]);
  const [filter, setFilter] = useState('');

  // Executed every first render
  useEffect(() => {
    getData();
  }, []);

  // Executed only when filter changes
  useEffect(() => {
    getDataByTypes(filter);
  }, [filter]);

  // Get data
  const getData = async () => {
    const uri = 'https://xxx';

    try {
      const response = await axios.get(uri);
      setData(response.data...);
    } catch (error) {
      console.log(error);
    }
  };

  // Get data by types
  const getDataByTypes = async (filter) => {
    const uri = `https://xxx/type/${filter}...`;

    if (filter) {
      try {
        const response = await axios.get(uri);
        setData(response.data...);
      } catch (error) {
        console.log(error);
      }
    }
  };

  return (
    <div className="main">
      <PokemonFilter filter={filter} setFilter={setFilter} />
      <div className="container">
        <div className="cards-container">
          {data.map((d) => (
            <PokemonCard key={d.name} data={d} />
          ))}
        </div>
      </div>
    </div>
  );
}

PokemonCard.js

import React, { useState, useEffect } from 'react';
import axios from 'axios';

export default function PokemonCard({ data }) {
  const [pokemons, setPokemons] = useState();

  useEffect(() => {
    getPokemons(data);
  }, [data]);

  // Get Pokemons
  const getPokemons = async (data) => {
    const uri = `https://xxx/pokemon/${data.name}/`;

    try {
      const response = await axios.get(uri);
      setPokemons(response.data...);
    } catch (error) {
      console.log(error);
    }
  };

  return (
    <div>
      {pokemons && (
        <div className="card">
          <img src={pokemons.sprites.front_default} alt={pokemons.name} />
          <p>{pokemons.name}</p>
          <p>{pokemons.abilities[0].ability.name}</p>
          <p>{pokemons.types[0].type.name}</p>
        </div>
      )}
    </div>
  );
}

PokemonFilter.js

import React, { useState, useEffect } from 'react';
import axios from 'axios';

export default function PokemonFilter({ filter, setFilter }) {
  const [types, setTypes] = useState([]);

  useEffect(() => {
    getType();
  }, []);

  // Get Type
  const getType = async () => {
    const uri = 'https://xxx/type/';

    try {
      const response = await axios.get(uri);
      setTypes(response.data.results....);
    } catch (error) {
      console.log(error);
    }
  };

  const handleFilter = (e) => {
    setFilter(e.target.value);
  };

  return (
    <select onChange={handleFilter} value={filter}>
      <option>Filter by type</option>
      {types.map((type) => {
        return (
          <option key={type.name} value={type.name}>
            {type.name}
          </option>
        );
      })}
    </select>
  );
}
于 2021-09-06T18:08:16.163 回答