You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在React宝可梦列表组件中实现搜索功能?附现有代码

Adding Search Functionality to Your Pokemon List Component

Hey there! Let's get that search feature up and running for your Pokemon list. Your core idea—filtering Pokemon by name and updating the state to reflect results—is totally correct. Let's walk through the changes needed step by step, fixing a couple of existing state management issues along the way.

Step 1: Update Component State

First, we need to add two new state properties to support search:

  • searchTerm: Stores the user's input from the search box
  • originalPokemon: Keeps a copy of the full, unfiltered Pokemon data (so we can always revert to it when filtering or switching views)

Modify your state like this:

state = {
  url: "https://pokeapi.co/api/v2/pokemon?limit=21&offset=0",
  pokemon: null,
  originalPokemon: null, // New: Holds unfiltered raw data
  buttonsPage: true,
  searchTerm: "" // New: Tracks search input text
};

Step 2: Save Original Pokemon Data

Every time you fetch Pokemon data, you need to save the raw results to originalPokemon alongside updating pokemon. This ensures we never lose the full dataset when applying filters.

Update componentDidMount:

async componentDidMount() {
  const res = await axios.get(this.state.url);
  this.setState({ 
    pokemon: res.data["results"],
    originalPokemon: res.data["results"] // Save initial page data
  });
}

Update allData:

(Note: We're fixing the direct state modification here—always use setState instead of changing this.state directly!)

async allData() {
  const res = await axios.get(
    "https://pokeapi.co/api/v2/pokemon?limit=2000&offset=0"
  );
  this.setState({ 
    pokemon: res.data["results"],
    originalPokemon: res.data["results"], // Save full dataset
    buttonsPage: false 
  });
  // Remove forceUpdate()—setState automatically triggers re-renders
}

Update pageView:

async pageView() {
  const res = await axios.get(
    "https://pokeapi.co/api/v2/pokemon?limit=21&offset=0"
  );
  this.setState({ 
    pokemon: res.data["results"],
    originalPokemon: res.data["results"], // Save page data
    buttonsPage: true 
  });
}

Fix nextPage and previousPage:

These methods also need to update originalPokemon when fetching new pages:

async nextPage() {
  const res = await axios.get(this.state.url);
  const nextUrl = res.data.next;
  const res2 = await axios.get(nextUrl);
  this.setState({ 
    url: nextUrl,
    pokemon: res2.data["results"],
    originalPokemon: res2.data["results"] // Save new page data
  });
}

async previousPage() {
  const res = await axios.get(this.state.url);
  const prevUrl = res.data.previous || "https://pokeapi.co/api/v2/pokemon?limit=21&offset=0";
  const res2 = await axios.get(prevUrl);
  this.setState({ 
    url: prevUrl,
    pokemon: res2.data["results"],
    originalPokemon: res2.data["results"] // Save new page data
  });
}

Step 3: Add the Search Input

Insert a search field in your render method, right above the Pokemon list. We'll bind an onChange handler to update the search state:

// Add this inside your render() return, before the Pokemon list
<div className="mt-2 ml-2">
  <input
    type="text"
    placeholder="Search Pokemon by name..."
    className="form-control w-25"
    value={this.state.searchTerm}
    onChange={(e) => this.handleSearch(e.target.value)}
  />
</div>

Step 4: Implement the Search Handler

Add a handleSearch method to filter Pokemon based on the input term and update the state:

handleSearch = (term) => {
  const lowerCaseTerm = term.toLowerCase();
  this.setState({ searchTerm: lowerCaseTerm });

  let filteredPokemon;
  if (lowerCaseTerm === "") {
    // Revert to original data when search is empty
    filteredPokemon = [...this.state.originalPokemon];
  } else {
    // Filter Pokemon by name (case-insensitive)
    filteredPokemon = this.state.originalPokemon.filter(pokemon => 
      pokemon.name.toLowerCase().includes(lowerCaseTerm)
    );
  }

  this.setState({ pokemon: filteredPokemon });
};

Key Notes & Fixes

  • Never modify state directly: Earlier you used this.state.buttonsPage = false—this breaks React's state management rules. Always use setState() to update state, as it triggers component re-renders automatically (no need for forceUpdate()).
  • Preserve original data: Storing originalPokemon ensures we don't overwrite the full dataset with filtered results, letting us easily revert back when needed.
  • Case insensitivity: Converting both the search term and Pokemon names to lowercase ensures the search works regardless of how the user types (e.g., "pikachu", "Pikachu", or "PIKACHU" all return the same result).

Final Full Component Code

Here's the complete component with all changes applied:

import React, { Component} from "react";
import axios from "axios";
import PokemonCard from "./PokemonCard";
import loading from "../layout/images/loading.gif";

export default class PokemonList extends Component {
  state = {
    url: "https://pokeapi.co/api/v2/pokemon?limit=21&offset=0",
    pokemon: null,
    originalPokemon: null,
    buttonsPage: true,
    searchTerm: ""
  };

  async componentDidMount() {
    const res = await axios.get(this.state.url);
    this.setState({ 
      pokemon: res.data["results"],
      originalPokemon: res.data["results"]
    });
  }

  async allData() {
    const res = await axios.get(
      "https://pokeapi.co/api/v2/pokemon?limit=2000&offset=0"
    );
    this.setState({ 
      pokemon: res.data["results"],
      originalPokemon: res.data["results"],
      buttonsPage: false 
    });
  }

  async pageView() {
    const res = await axios.get(
      "https://pokeapi.co/api/v2/pokemon?limit=21&offset=0"
    );
    this.setState({ 
      pokemon: res.data["results"],
      originalPokemon: res.data["results"],
      buttonsPage: true 
    });
  }

  async nextPage() {
    const res = await axios.get(this.state.url);
    const nextUrl = res.data.next;
    const res2 = await axios.get(nextUrl);
    this.setState({ 
      url: nextUrl,
      pokemon: res2.data["results"],
      originalPokemon: res2.data["results"]
    });
  }

  async previousPage() {
    const res = await axios.get(this.state.url);
    const prevUrl = res.data.previous || "https://pokeapi.co/api/v2/pokemon?limit=21&offset=0";
    const res2 = await axios.get(prevUrl);
    this.setState({ 
      url: prevUrl,
      pokemon: res2.data["results"],
      originalPokemon: res2.data["results"]
    });
  }

  handleSearch = (term) => {
    const lowerCaseTerm = term.toLowerCase();
    this.setState({ searchTerm: lowerCaseTerm });

    let filteredPokemon;
    if (lowerCaseTerm === "") {
      filteredPokemon = [...this.state.originalPokemon];
    } else {
      filteredPokemon = this.state.originalPokemon.filter(pokemon => 
        pokemon.name.toLowerCase().includes(lowerCaseTerm)
      );
    }

    this.setState({ pokemon: filteredPokemon });
  };

  render() {
    return (
      <>
        {/* Search Input */}
        <div className="mt-2 ml-2">
          <input
            type="text"
            placeholder="Search Pokemon by name..."
            className="form-control w-25"
            value={this.state.searchTerm}
            onChange={(e) => this.handleSearch(e.target.value)}
          />
        </div>

        {/* View Toggle Buttons */}
        <button
          onClick={() => this.allData()}
          type="button"
          className="btn btn-secondary mt-2 ml-2 mb-2"
          style={{ display: this.state.buttonsPage ? "block" : "none" }}
        >
          All pokemons
        </button>
        <button
          onClick={() => this.pageView()}
          type="button"
          className="btn btn-secondary mt-2 ml-2 mb-2"
          style={{ display: this.state.buttonsPage ? "none" : "block" }}
        >
          Back to page view
        </button>

        {/* Pokemon List or Loading State */}
        {this.state.pokemon ? (
          <div className="row">
            {this.state.pokemon.map((pokemon) => (
              <PokemonCard
                key={pokemon.name}
                pokemonName={pokemon.name}
                url={pokemon.url}
              />
            ))}
          </div>
        ) : (
          <img
            src={loading}
            style={{ width: "15em", height: "15em" }}
            className="card-img-top mx-auto mt-2"
          />
        )}

        {/* Pagination Buttons */}
        <div
          className="container-fluid mx-auto"
          style={{ display: this.state.buttonsPage ? "block" : "none" }}
        >
          <button
            onClick={() => this.previousPage()}
            type="button"
            className="btn btn-secondary mt-4 ml-2 mb-2"
          >
            Previous page
          </button>
          <button
            onClick={() => this.nextPage()}
            type="button"
            className="btn btn-secondary mt-4 ml-2 mb-2 float-right"
          >
            Next page
          </button>
        </div>
      </>
    );
  }
}

Now your search feature should work smoothly: as you type in the input box, the list will automatically filter to show only Pokemon whose names include your search term. Clearing the input will bring back the full list (or current page, depending on your view mode).

内容的提问来源于stack exchange,提问作者Mladen

火山引擎 最新活动