user15498557
user15498557

Reputation: 127

React - filtering items from an array in any order

I'm sorry that this question has been asked in a similar fashion. I've read through a lot of similar threads but just don't know enough to apply it to my little project. I'm very new to React so I would appreciate any help!

Getting to it, I'm making a pokemon game where someone can hit click buttons to filter down a JSON list of pokemon. They can go by type,weaknessess,etc.

But I'm a little confused in keeping a 'global array' if that's the right word. It may be something I don't understand with React's useState.

Here's my code

import React, { useState, useEffect } from "react";
import "./App.css";
import PokemonLibrary from "./data/PokemonList.json";

export default function App() {
  const [pokemonData, setPokemonData] = React.useState(PokemonLibrary.pokemon);
  const pokeListCopy = PokemonLibrary.pokemon;


const filterTypeOne = () => { //filter type
const myFilteredPoke = pokeListCopy.filter((pokeType) => {
  return pokeType.type.includes("Grass");
});
console.log(myFilteredPoke); // shows array of objects of left over pokemon

setPokemonData(myFilteredPoke);
  };

const filterWeakness = () => { //filter weakness
    const myFilteredPoke = pokeListCopy.filter((pokeType) => {
      return pokeType.weaknesses.includes("Ice");
    });
    setPokemonData(myFilteredPoke);
      };

  return (
    <div className="App">
      <h1>Pokemon Selector!</h1>
      <div>
        <button onClick={filterTypeOne}>Filter Grass</button>
        <button onClick={filterWeakness}>Weak to Ice</button>
      </div>
      {pokemonData &&
        pokemonData.map((poke) => (
          <p key={poke.id}>
            #{poke.num} | {poke.name} | {poke.type[0]} {poke.type[1]}
            <img src={poke.img} alt="Pokemon Images"></img>
          </p>
        ))}
    </div>
  );
}

My question is, how do I keep a consistent array for these two functions (and more) to pull the same data from? I'd like it to be able to be filtered in either order. But currently, these filter separate arrays. I've played with it a lot using normal JavaScript, but I can't quite figure this out.

I hope that was enough information. Please let me know if this didn't make sense! I'd appreciate any guidance. Thank you.

Upvotes: 3

Views: 578

Answers (1)

kca
kca

Reputation: 6055

Problem

You are facing this problem because you try to set the state of the list in an "imperative" manner, but React is meant to be used more "declarative". That means, what you try here is like:

  • "if I click the button, change the list to contain the items that contain 'Grass'",

but how you should use React is:

  • "if I click the button, then the state of my component should be that the list only contains items with 'grass'"

That might sound like the same, but there is a subtle difference. React is good in changing a state dependent on some other state. You might say "but that's what I'm trying to do, changing the state of the list", but then you have tell the full story

  • "if I click the button, and the list is not filtered already, and maybe contains the items X, then change the list to contain the items that contain 'Grass', unless ..."

This becomes quite complicated, especially comparing contents of lists and components.

Solution

There are different solutions to your problem, but what you should do is basically something like:

  • set the component state to describe what you want
  • have other parts of your program (and React) take care to give you a the list dependent on this description

e.g.:

const [pokemonData, setPokemonData] = React.useState(PokemonLibrary.pokemon);
const [listState, setListState] = React.useState({ type: '', weekness: '' });

useEffect(() => {
  let newList = pokemonData;
  if( listState.type === 'Grass' ){
    newList = newList.filter( ... );
  }
  if( listState.weekness === 'Ice' ){
    newList = newList.filter( ... );
  }
  setPokemonData(newList);
}, listState );

return (
  <div>
    <button onClick={()=>{ setListState({ ...listState, type: 'Grass' }) }}>Filter Grass</button>
    { pokemonData.map( (poke) => ... ) }
  </div>
);

(This code is not very elegant and would not even work, and should only illustrate the idea. From here on there are several ways how to implement the filtering mechanism)

Upvotes: 1

Related Questions