I'd like to pass filteredData
in the child Component PokemonSearchBar.jsx so that I can use it in the parent component PokemonList.jsx to map only filtered Pokemon. In other words, when I do a search, the search component will compare the characters from the e.target.value
of my input to the ones of the data
array (filled with all the Pokemon names), which is passed as a prop from the parent component PokemonList.jsx. If there are some characters in common between the e.target.value
and the data
array, the filteredData
will receive only the Pokemon with characters in common with e.target.value
from the input.
Is there any way that I can pass the filteredData
state to the parent component as a prop ?
Here is the child component PokemonSearchBar.jsx:
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import './PokemonSearchBar.css';
import SearchIcon from '@mui/icons-material/Search';
import CloseIcon from '@mui/icons-material/Close';
export default function PokemonSearchBar({ placeholder, data }) {
const [filteredData, setFilteredData] = useState([]);
const [wordEntered, setWordEntered] = useState("");
const handleFilter = (e) => {
const searchWord = e.target.value;
setWordEntered(searchWord);
const newFilter = data.filter(value => {
return (value.name.toLowerCase().includes(searchWord.toLowerCase()));
});
if(searchWord === "") {
setFilteredData([]);
} else {
setFilteredData(newFilter);
}
}
const clearInput = (e) => {
setWordEntered("");
setFilteredData([]);
}
return (
<div className='search'>
<div className="row" style={{ display: 'flex', justifyContent: 'center' }}>
<div className="col-3"></div>
<div className="col-6">
<div className="searchInputs" style={{ display: 'flex', justifyContent: 'center'}}>
<input type="text" className="form-control rounded" placeholder={placeholder} onChange={handleFilter} value={wordEntered} />
{filteredData.length === 0 ? <button type="button" className="btn btn-primary"><SearchIcon /></button> : <button type="button" className="btn btn-warning"><CloseIcon onClick={clearInput} /></button>}
</div>
</div>
<div className="col-3"></div>
</div>
{filteredData.length !== 0 &&
<div className="dataResult mx-auto" style={{ display: 'flex', justifyContent: 'center' }}>
<ul>
{filteredData.slice(0, 15).map((pokemon) => {
return (<li style={{ listStyleType: 'none', textDecoration: 'none'}} className="dataItem"><Link to="/" className="a" style={{ textDecoration: 'none', color: 'black' }}>{pokemon.name}</Link></li>)
})}
</ul>
</div>
}
</div>
)
}
Here is the parent component PokemonList.jsx:
import React, { Component } from 'react'
import axios from 'axios'
import ReactPaginate from 'react-paginate';
import PokemonCard from './PokemonCard';
import PokemonSearchBar from './PokemonSearchBar';
export default class PokemonList extends Component {
state = {
url: "https://pokeapi.co/api/v2/pokemon?limit=100000&offset=0",
pokemon: [], // on créé un objet (qui va devenir un tableau) pokemon null c'est où on va enregistrer le json
pageNumber: 0,
pokemonsPerPage: 16
};
async componentDidMount() {
const res = await axios.get(this.state.url); // on attend de récupérer les pokemon avant d'exectuer la suite de la fonction
console.log(res.data['results']);
this.setState({pokemon: res.data['results']}); // on passe un objet dans le state et on affecte à pokemon le tableau résultats de la data de l'url
// setState re-render seulement les éléments qu'il faut re-rendre
};
render() {
console.log(this.state.pokemon);
const pagesVisited = this.state.pageNumber * this.state.pokemonsPerPage;
const displayUsers = this.state.pokemon
.slice(pagesVisited, pagesVisited this.state.pokemonsPerPage)
.map((pokemon) => {
return (
<PokemonCard
key={pokemon.name} // on utilise le props name comme clé unique car chaque nom de pokemon est unique
name={pokemon.name} // c'est le props name dans le pokemonCard
url={pokemon.url} // c'est le props url dans le pokemonCard
/> // on génère autant de cartes qu'il y a de pokemons dans le tableau results
)
});
const pageCount = Math.ceil(this.state.pokemon.length / this.state.pokemonsPerPage); // ceil() permet d'arrondir au supérieur
const changePage = ({selected}) => {
this.setState({pageNumber: selected})
}
return (
<React.Fragment>
<PokemonSearchBar data={this.state.pokemon} placeholder="Enter a Pokemon name" />
<br />
{this.state.pokemon ? ( // s'il y a quelquechose dans le state
<div className='row'>
{displayUsers}
<div style={{ display: 'flex', justifyContent: 'center'}}>
<ReactPaginate
previousLabel={"previous"}
nextLabel={"Next"}
pageCount={pageCount}
onPageChange={changePage}
containerClassName={"paginationButtons"}
previousLinkClassName={"previousButton"}
nextLinkClassName={"nextButton"}
disabledClassName={"paginationDisabled"}
activeClassName={"paginationActive"}
/>
</div>
</div>
) : (<h2>Loading Pokemon</h2>)}
{/* S'il n'y a rien dans le state afficher que le pokemon est en train de charger */}
</React.Fragment>
)
}
}
CodePudding user response:
In React there are essentially 3 ways to send data up:
- Via callback passed as a prop;
- Via Store (Redux or Context API, or other)
- Via native custom Javascript events
Your use-case can be implemented via prop:
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import './PokemonSearchBar.css';
import SearchIcon from '@mui/icons-material/Search';
import CloseIcon from '@mui/icons-material/Close';
export default function PokemonSearchBar({ placeholder, data, onDataFiltered }) {
const [filteredData, setFilteredData] = useState([]);
const [wordEntered, setWordEntered] = useState("");
const handleFilter = (e) => {
const searchWord = e.target.value;
setWordEntered(searchWord);
const newFilter = data.filter(value => {
return (value.name.toLowerCase().includes(searchWord.toLowerCase()));
});
if(searchWord === "") {
setFilteredData([]);
onDataFiltered([])
} else {
setFilteredData(newFilter);
onDataFiltered(newFilter)
}
}
const clearInput = (e) => {
setWordEntered("");
setFilteredData([]);
}
return (
<div className='search'>
<div className="row" style={{ display: 'flex', justifyContent: 'center' }}>
<div className="col-3"></div>
<div className="col-6">
<div className="searchInputs" style={{ display: 'flex', justifyContent: 'center'}}>
<input type="text" className="form-control rounded" placeholder={placeholder} onChange={handleFilter} value={wordEntered} />
{filteredData.length === 0 ? <button type="button" className="btn btn-primary"><SearchIcon /></button> : <button type="button" className="btn btn-warning"><CloseIcon onClick={clearInput} /></button>}
</div>
</div>
<div className="col-3"></div>
</div>
{filteredData.length !== 0 &&
<div className="dataResult mx-auto" style={{ display: 'flex', justifyContent: 'center' }}>
<ul>
{filteredData.slice(0, 15).map((pokemon) => {
return (<li style={{ listStyleType: 'none', textDecoration: 'none'}} className="dataItem"><Link to="/" className="a" style={{ textDecoration: 'none', color: 'black' }}>{pokemon.name}</Link></li>)
})}
</ul>
</div>
}
</div>
)
}
You will then declare it like this:
<PokemonSearchBar
data={this.state.pokemon}
placeholder="Enter a Pokemon name"
onDataFiltered={(newData) => {
console.log("do something with the new data", newData);
}}
/>