When I click on a specific button I want to capture the {country} prop associated with it. I tired the following
import React, { useState, useEffect } from 'react'
import axios from 'axios'
// ====================================================================[SEARCH-BAR]=======================================================
// search component
const SearchBar = (props) => {
// console.log(props);
const { searchString, searchOnChangeEventHandler } = props
return (
<>
<form>
<label>Search </label>
<input type='text' placeholder='type to search...' value={searchString} onChange={searchOnChangeEventHandler} />
</form>
</>
)
}
// ================================================================[COUNTRY_CARD]==========================================================
// countryCard component
const CountryCard = (props) => {
console.log(props);
return (
<div>
<p>countryName</p>
<p>capital</p>
<p>population</p>
<p>languages</p>
<ul>
<li>item</li>
<li>item</li>
</ul>
<p>image flag</p>
</div>
)
}
// ===================================================================[DISPLAY]===========================================================
// display component
const Display = (props) => {
const [showCountryCard, setShowCountryCard] = useState(false)
const [thisCountry, setThisCountry] = useState({})
// console.log(props);
const { countries, searchString } = props
// console.log(countries);
// eslint-disable-next-line eqeqeq
// searchString empty
if (searchString == false) {
return (
<>
<div>
<span>Type in SearchBar for a country...</span>
</div>
</>
)
}
// to count number of matches
const filteredResultsCount = countries.filter(country => country.name.toLowerCase().includes(searchString.toLowerCase())).length
// function to filterCountries
const filteredResults = (searchString, countries) => countries.filter(country => {
return country.name.toLowerCase().includes(searchString.toLowerCase())
})
// RENDER CONDITIONS
// searchString return <= 10 matches && >1 match
// event handler for show-btn
const showCardEventHandler = (event) => {
console.log(event.target.parentElement);
setShowCountryCard(!showCountryCard)
}
if (filteredResultsCount <= 10 && filteredResultsCount > 1) {
return (
<>
<ul>
{
filteredResults(searchString, countries).map(country =>
<li
key={country.numericCode}
country={country}
>
<span>{country.name}</span>
<button
value={showCountryCard}
onClick={showCardEventHandler}
>show</button>
</li>
)
}
</ul>
{
showCountryCard ? <p>show country card</p> : null
}
</>
)
}
// searchString returns >10 matches
if (filteredResultsCount > 10) {
return (
<span>{filteredResultsCount} matches!, please refine your search...</span>
)
}
// searchString returns ===1 match
if (filteredResultsCount === 1) {
return (
<>
{
filteredResults(searchString, countries).map(country => <CountryCard key={country.numericCode} country={country} />)
}
</>
)
}
// invalid searchString
if (filteredResultsCount === 0) {
return (
<span><strong>{filteredResultsCount} matches!</strong> please refine your search...</span>
)
}
}
// ===================================================================[APP]==============================================================
// app component
const App = () => {
// to store countries
const [countries, setCountries] = useState([])
// to fetch data from
const url = 'https://restcountries.eu/rest/v2/all'
useEffect(() => {
// console.log('effect');
axios
.get(url)
.then(response => {
// console.log('promise fulfilled');
const countries = response.data
// array of objects
setCountries(countries)
})
}, [])
// console.log('countries', countries.length);
// console.log(countries);
// to store search string
const [searchString, setSearchString] = useState('')
// event handler search input
const searchOnChangeEventHandler = (event) => setSearchString(event.target.value)
return (
<>
<h1>Countries Data</h1>
<SearchBar searchString={searchString} searchOnChangeEventHandler={searchOnChangeEventHandler} />
<br />
<Display countries={countries} searchString={searchString} />
</>
)
}
export default App
Please take a look at <Display/>
component and in particular I'm trying to work on this part
const showCardEventHandler = (event) => {
console.log(event.target.parentElement);
setShowCountryCard(!showCountryCard)
}
if (filteredResultsCount <= 10 && filteredResultsCount > 1) {
return (
<>
<ul>
{
filteredResults(searchString, countries).map(country =>
<li
key={country.numericCode}
country={country}
>
<span>{country.name}</span>
<button
value={showCountryCard}
onClick={showCardEventHandler}
>show</button>
</li>
)
}
</ul>
{
showCountryCard ? <p>show country card</p> : null
}
</>
)
}
I want to be able to render a list of countries if they are more than 10 and allow a user to click on a specific country, which then will be used to render the <CountryCard/>
component.
If there is only 1 matching value from search then I will directly display the country card component. The second functionality works.
After the following refactor the first functionality works, but Ima little confused as to why so I'm adding on to the post. This is the component being rendered and now I'm passing country prop onClick, like so
if (filteredResultsCount <= 10 && filteredResultsCount > 1) {
return (
<>
<ul>
{filteredResults(searchString, countries).map((country) => (
<li key={country.numericCode} country={country}>
<span>{country.name}</span>
<button
value={showCountryCard}
onClick={() => toggleCardEventHandler(country)}>
{showCountryCard ? 'hide' : 'show'}
</button>
</li>
))}
</ul>
{showCountryCard ? <CountryCard country={country} /> : null}
</>
);
}
The event handler is as follows
const toggleCardEventHandler = (country) => {
// console.log(country);
setShowCountryCard(!showCountryCard);
setCountry(country)
};
This works properly.
My question is, when I change the eventHandler onClick={toggleCardEventHandler(country)}
it breaks, but shouldnt it be accessible through closure?
Also, if I change the code to this
onClick={() => {
toggleCardEventHandler()
setCountry(country)
}}
The code works the way I want but which is a better way to pass the value to the toggleCardEventHandler()
and set the country there or to do it like this?
As I understand it you want to pass the country.name
to your showCardEventHandler.
Update showCardEventHandler
so it takes the event and the country name:
const showCardEventHandler = (event, countryName) => {
console.log(countryName);
setShowCountryCard(!showCountryCard)
}
Now pass the countryname to the function:
<li
key={country.numericCode}
country={country}
>
<span>{country.name}</span>
<button
value={showCountryCard}
onClick={e => showCardEventHandler(e, country.name)}
>show</button>
</li>
Since you are not using the event in showCardEventHandler
you can remove it from the signature
const showCardEventHandler = (countryName) => {}
and call it with onClick={() => showCardEventHandler(country.name)}