Strange behaviour when passing variable down to child component - javascript

I am seeing some strange behaviour when I am trying to pass down a variable to a child component in react. When I console.log just before the return statement (so console.log(1)) in the parent component the data is correct, however when I console.log in the child component (so console.log(2)) the data has changed??
I have a suspicion that it relates to the randomSelect() function but again when console logging out this looks to only be called twice (as expected).
I have pasted a 'playerOneId' in directly (avoiding using the randomSelect() function) and the data shows correctly in the child component when doing this, hence my suspicion around the randomSelect() function. It could be unrelated but not sure.
A gold medal to anyone can answer this one as it has had me for hours now and I've run out of ideas.
PARENT COMPONENT:
const Board = () => {
const [starships, setStarships] = useState([]);
const [playerSelected, setPlayerSelected] = useState(false);
const [result, setResult] = useState('');
const [playerScore, setPlayerScore] = useState(0);
const [computerScore, setComputerScore] = useState(0);
const STARSHIP_QUERY = `{
allStarships {
starships {
id
name
starshipClass
maxAtmospheringSpeed
costInCredits
passengers
filmConnection {
films {
title
}
}
}
}
}
`
useEffect(() => {
fetch('https://connectr-swapi.herokuapp.com/', {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({query: STARSHIP_QUERY})
})
.then(response => response.json())
.then(data => setStarships(data.data.allStarships.starships))
.catch(error => console.log({'Error': error}))
},[])
const randomSelect = () => {
const random = Math.floor(Math.random() * starShipIds.length);
const selectedId = starShipIds[random];
return selectedId;
}
const starShipIds = starships.map(ship => ship.id)
const valueOneID = randomSelect();
const valueTwoID = randomSelect();
const playerOneId = valueOneID;
const computerId = valueTwoID;
const playerOneShip = starships.filter(ship => ship.id === playerOneId) ;
const computerShip = starships.filter(ship => ship.id === computerId);
const catergorySelect = (key, value) => {
let computerValue = key === 'filmConnection' ? computerShip[0][key].films.length : computerShip[0][key];
if (value > computerValue) {
setResult('You Win!');
setPlayerScore(playerScore + 1)
}
if (value === computerValue) setResult('You Draw!');
if (value < computerValue) {
setResult('You Lose!');
setComputerScore(computerScore + 1)
}
setPlayerSelected(true);
}
console.log(1, playerOneShip[0]); // data is showing correctly
return (
<div className="background">
<div className="row">
<div className="col-12 col-sm-4">
{playerOneShip.length &&
<GameCard
ship={playerOneShip[0]} // data passed in
player='player-one'
select={catergorySelect}
/>
}
{playerSelected &&
<Score
score={playerScore}
colour="white"
/>
}
</div>
<div className="col-12 col-sm-4">
<div className="row">
<h1>{result}</h1>
</div>
<div className="row">
<DefaultBtn
text="START AGAIN"
colour="white"
/>
</div>
</div>
<div className="col-12 col-sm-4">
{playerSelected &&
<React.Fragment>
<div>
{computerShip.length &&
<GameCard
ship={computerShip[0]}
player='computer'
catergorySelect={catergorySelect}
/>
}
</div>
<div>
<Score
score={computerScore}
colour="white"
/>
</div>
</React.Fragment>
}
</div>
</div>
</div>
)
}
CHILD COMPONENT:
const GameCard = props => {
const [selected, setSelected] = useState(0);
const [disableCategory, setDisableCategory] = useState(false);
const {
ship,
player,
select,
} = props;
console.log(2, ship) // different data is showing
const categories = Object.entries(props.ship).map(([key, value], index) => {
const choosenCategory = selected === index ? 'selected' : '';
const disableButton = disableCategory ? 'disable' : '';
switch (key) {
case 'maxAtmospheringSpeed':
return <li className={`card ${player} ${choosenCategory} ${disableButton}`} onClick={(() => { select(key, value); setSelected(index); setDisableCategory(true)})} key={index}>{`Maximum Speed: ${value}`}</li>
case 'costInCredits':
return <li className={`card ${player} ${choosenCategory} ${disableButton}`} onClick={(() => { select(key, value); setSelected(index); setDisableCategory(true)})} key={index}>{`Cost In Credits: ${value}`}</li>
case 'passengers':
return <li className={`card ${player} ${choosenCategory} ${disableButton}`} onClick={(() => { select(key, value); setSelected(index); setDisableCategory(true)})} key={index}>{`Number Of Passengers: ${value}`}</li>
case 'filmConnection':
return <li className={`card ${player} ${choosenCategory} ${disableButton}`} onClick={(() => { select(key, value.films.length); setSelected(index); setDisableCategory(true)})} key={index}>{`Number Of films: ${value.films.length}`}</li>
default:
return null
}
});
return (
<div className="card">
<img className="card-image" src="assets/img/starships/2.jpg" />
<div className="card-body">
<p className="card-title">{`Ship Name: ${ship.name}`}</p>
<p className="card-sub-title">{`Class: ${ship.starshipClass}`}</p>
<ul>
{categories}
</ul>
</div>
</div>
)
}

It's probably a reference issue, the variable passed in props is updated by another render in the parent.
A way of fixing it could be to put all this section of code in a useEffect depending on the loading of the starships:
const starShipIds = starships.map(ship => ship.id)
const valueOneID = randomSelect();
const valueTwoID = randomSelect();
const playerOneId = valueOneID;
const computerId = valueTwoID;
const playerOneShip = starships.filter(ship => ship.id === playerOneId) ;
const computerShip = starships.filter(ship => ship.id === computerId);
It could look like this:
useEffect(() => {
const starShipIds = starships.map(ship => ship.id)
const valueOneID = randomSelect(starShipIds);
const valueTwoID = randomSelect(starShipIds);
const playerOneId = valueOneID;
const computerId = valueTwoID;
setPlayerOneShip(starships.filter(ship => ship.id === playerOneId));
setComputerShip(starships.filter(ship => ship.id === computerId));
},[starships])
For this you need to create a state for player ship and computer ship and replace previous usage of these, like in my example above.
Also, you should pass the starship ids to random select as a parameter and not use a const and assume it has the correct value because it is in the scope of the function.

Related

Mapping through an array not rendering [duplicate]

This question already has answers here:
When should I use a return statement in ES6 arrow functions
(6 answers)
Closed 23 days ago.
I'm trying to render some cards through the mapping of an array.
My parent component seems to be consuming the information but the cards are still not rendering. In the example below I went back to basics and I'm not including the cards, I'm just trying to render an h1 in the parent component, and that also doesn't work:
This is my parent component:
export const Home = () => {
const { store, actions } = useContext(Context);
const data_types = Object.keys(store.results);
useEffect(() => {
for (let data_type of data_types) {
actions.getData(data_type)
}
}, []);
return (
<div className="text-center mt-5">
{data_types.map((data_type) => {
store.results[data_type].map((item, index) => {
<div className="container mb-4">
{console.log(index, item)}
<h1>{item}</h1>
</div>
})
This is how I fetch and store the data:
const getState = ({ getStore, getActions, setStore }) => {
return {
store: {
results: {
people: [],
planets: [],
starships: [],
vehicles: [],
}
},
actions: {
getData: async (data_type) => {
const local_results = JSON.parse(localStorage.getItem("results"));
if (local_results !== null && local_results[data_type].length > 0) {
console.log(`The data type ${data_type} is already in local storage`)
let result = {}
result[data_type] = [...local_results[data_type]]
setStore({
results: {
...getStore().results,
...result
}
})
return
}
const baseURL = `https://swapi.dev/api/${data_type}/`;
try {
const response_1 = await fetch(`${baseURL}`);
const data_1 = await response_1.json();
const response_2 = await fetch(`${data_1.next}`);
const data_2 = await response_2.json();
let result = {}
result[data_type] = [...data_1.results, ...data_2.results]
setStore({
results: {
...getStore().results,
...result
}
})
localStorage.setItem("results", JSON.stringify(getStore().results));
} catch (error) {
console.log(`Error loading message from https://swapi.dev/api/${data_type}/`, error);
}
}
This is the console print out:
I've also tried using a React State like this:
export const Home = () => {
const { store, actions } = useContext(Context);
const [arr, setArr] = useState([])
const data_types = Object.keys(store.results);
useEffect(() => {
for (let data_type of data_types) {
actions.getData(data_type)
}
setArr(store.results);
}, []);
return (
<div className="text-center mt-5">
{data_types.map((data_type) => {
arr !== "undefined" && data_type in arr ? arr[data_type].map((item, index) => {
<div className="container mb-4">
{console.log(index, item)}
<h1>{item}</h1>
</div>
}) : null
})
}
</div>
)
}
Your map function is returning an array of undefineds because you don't return anything in the arrow function.
either add the return keyword
return (
<div className="text-center mt-5">
{data_types.map((data_type) => {
return store.results[data_type].map((item, index) => {
<div className="container mb-4">
{console.log(index, item)}
<h1>{item}</h1>
</div>
})
or remove the curly brackets
return (
<div className="text-center mt-5">
{data_types.map((data_type) => {
return store.results[data_type].map((item, index) => (
<div className="container mb-4">
{console.log(index, item)}
<h1>{item}</h1>
</div>
))

Show loading spinner tilll the image is fully loaded (prevent printing image effect) React.js

I have background image that loads as it is like printing even if i'm using loading spinner (to make the content visible only when the image is got from the API). How can i prevent it, i tried using loading="lazy" properties but still not success.
App.js (newLoader state is used to set the image on response from api)
import { useEffect, useState } from 'react';
import './App.css';
import Forcast from './components/Forcast';
import Loader from './components/Loader';
import Weather from './components/Weather';
function App() {
// States
const [loader, setLoader] = useState(true);
const [newLoader, setNewLoader] = useState(true);
const [lat, setLat] = useState(0);
const [long, setLong] = useState(0);
const [weatherData, setWeatherData] = useState("");
const [forcastData, setForcastData] = useState([]);
const [photos, setPhotos] = useState([]);
const [country, setCountry] = useState("");
useEffect(() => {
//Function to call apis for weather, forcast and unsplash
const weatehr = async () => {
// todayWeather
navigator.geolocation.getCurrentPosition((position) => {
setLat(position.coords.latitude);
setLong(position.coords.longitude);
})
if (lat && long !== 0) {
// getting the current weatehr
await fetch(`https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${long}&units=metrics&appid=${process.env.REACT_APP_API_KEY}`)
.then((res) => res.json())
.then((data) => {
setWeatherData(data)
})
.catch((err) => {
alert(err.message)
})
// getting the random image from unsplash
fetch(`https://api.unsplash.com/search/photos?query={landscape}&orientation=landscape&client_id=${process.env.REACT_APP_UNSPLASH_API_KEY}`)
.then((res) => res.json())
.then((data) => {
const imageUrlsAre = data.results.map((item) => item.urls.full)
const randomImage = Math.floor(Math.random() * imageUrlsAre.length);
setPhotos(imageUrlsAre[randomImage])
})
.then(() => {
setNewLoader(false)
})
.catch((err) => err)
// for forcast of 5 days
await fetch(`https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${long}&exclude=current,minutely,hourly,alerts&units=metrics&appid=${process.env.REACT_APP_API_KEY}`)
.then((res) => res.json())
.then((data) => {
setLoader(false)
// for getting country name
if (data && data.city) {
const coutntryForcast = data.city.country;
setCountry(coutntryForcast)
}
// for getting the forcast
const arrayForcast = data.list;
if (arrayForcast) {
let midday = []
for (let i = 0; i < arrayForcast.length; i++) {
let dates = arrayForcast[i].dt_txt.substring(10);
let days = arrayForcast[i].dt_txt.slice(0, 10);
let icon = arrayForcast[i].weather[0].icon;
let temp = arrayForcast[i].main
if ((dates.indexOf("12:00:00") > -1) === true) {
midday.push({ temp, days, icon })
}
}
midday.shift()
setForcastData(midday)
}
})
.catch((err) => {
alert(err.message)
})
}
}
weatehr()
}, [lat, long])
return (<>
{loader === true ? <Loader /> :
<>
{newLoader === true ? <Loader /> :
<div className="App">
<div className='image__wrapper'>
<img alt='background' src={photos && photos}
loading="lazy"
/>
<div className='overlay'>
</div>
</div>
<div className='app__body'>
<div className='today__weather'>
{weatherData.cod === 200 ? <Weather weatherData={weatherData} photo={photos} country={country} forcastData={forcastData} /> : <div className='error_message'> No weather</div>}
</div>
<div className='forcast'>
{weatherData.cod === 200 ? <Forcast weatherData={weatherData} forcastData={forcastData} /> : <div className='error_message'></div>}
</div>
</div>
</div>}
</>
}
</>
);
}
export default App;

React useEffect causing function to run 7 times. I am using a useCallback but it still runs to many times

The updateAddedState function with the console.log("running") is running 7 times on a page refresh/initial render.
I only want the updateAddedState function to run once when the addedItems state updates.
I only what the useEffect to run when the actual addedItems state has changed. What am I doing wrong??
export const DropdownMultiSelect = ({
data,
placeholder,
updateState,
}: IProps) => {
const [searchTerm, setSearchTerm] = useState<string>("");
const [filteredData, setFilteredData] = useState<IData[]>(data);
const [addedItems, setAddedItems] = useState<IData[]>([]);
const [placeholderValue, setPlaceholderValue] = useState<string>("");
const [inputActive, setInputActive] = useState<boolean>(false);
const onFocus = () => setInputActive(true);
const onBlur = () => {
setInputActive(false);
};
const updateAddedState = useCallback(() => {
console.log("running");
updateState(addedItems);
}, [updateState, addedItems]);
const handleFilter = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearchTerm(e.target.value);
};
const handleFilterData = useCallback(
(searchTerm: string) => {
let newFilter = data.filter((value) => {
return value.name.toLowerCase().includes(searchTerm.toLowerCase());
});
for (let i = 0; i < addedItems.length; i++) {
for (let j = 0; j < newFilter.length; j++) {
if (addedItems[i].id === newFilter[j].id) {
newFilter.splice(j, 1);
}
}
}
setFilteredData(newFilter);
},
[addedItems, data]
);
const addItem = (value: IData) => {
setAddedItems([...addedItems, value]);
setSearchTerm("");
handleFilterData("");
setInputActive(false);
};
const removeItem = (value: IData, e: React.MouseEvent) => {
e.preventDefault();
let newArray: IData[] = [];
for (let i = 0; i < addedItems.length; i++) {
newArray.push(addedItems[i]);
}
for (let i = 0; i < newArray.length; i++) {
if (value.id === newArray[i].id) {
newArray.splice(i, 1);
}
}
setAddedItems(newArray);
setInputActive(true);
};
useEffect(() => {
if (addedItems.length === 1) {
setPlaceholderValue(`${addedItems.length} vald`);
} else if (addedItems.length > 1) {
setPlaceholderValue(`${addedItems.length} valda`);
} else {
setPlaceholderValue(placeholder);
}
}, [addedItems, placeholderValue, placeholder]);
useEffect(() => {
handleFilterData(searchTerm);
}, [searchTerm, addedItems, handleFilterData]);
useEffect(() => {
let isMounted = true;
if (isMounted) {
if (addedItems) {
updateAddedState();
}
}
return () => {
isMounted = false;
};
}, [updateAddedState, addedItems]);
return (
<div id="dropdownMulti">
<section className="inputSection">
<input
type="text"
placeholder={placeholderValue}
className="inputSection__input"
onChange={handleFilter}
value={searchTerm}
onFocus={onFocus}
onBlur={onBlur}
/>
<div className="inputSection__icon-container">
{inputActive ? (
<AiOutlineUpCircle
onClick={() => setInputActive(false)}
className="inputSection__icon-container--up"
/>
) : (
<AiOutlineDownCircle className="inputSection__icon-container--down" />
)}
</div>
</section>
<section className="addedItems-section">
{inputActive &&
addedItems.map((addedItem) => {
return (
<div className="addedItem" key={addedItem.id}>
<p className="addedItem__item">{addedItem?.name}</p>
<button
data-testid="remove-btn"
className="addedItem__button"
onMouseDown={(e: React.MouseEvent) =>
removeItem(addedItem, e)
}
>
<AiOutlineCloseCircle />
</button>
</div>
);
})}
</section>
{inputActive && (
<ul className="dataResult">
{filteredData.slice(0, 10).map((value) => {
return (
<li
className="dataResult__item"
key={value.id}
tabIndex={0}
onMouseDown={() => addItem(value)}
>
{value.name}
</li>
);
})}
</ul>
)}
</div>
);
};
Any tips on how to cut the number of times it runs?
Try to remove React Strict Mode, it makes components render twice only in development, not in production. Put back on if it's the case.
Try removing updateState function from the dependency array of the useCallback function.
const updateAddedState = useCallback(() => {
console.log("running");
updateState(addedItems);
}, [addedItems]);
If it's still not working define the updateState function which comes with the props with the useCallback hook where the function is defined.

Add tag functionality adds tags to every item in list instead of just the one its meant to be added to

I am rendering a list of students and in the list I am adding a tag to the student the functionality works however the tag is added to every student instead of just the one that I specifically want the tag added to. I am also trying to filter my students by tags which i believe may be working properly except I am unable to be sure because every student gets the tags so the filter doesnt do much to it.
import React, { useState, useEffect } from 'react';
function StudentProfile() {
const [error, setError] = useState(null);
const [isLoaded, setIsLoaded] = useState(false);
const [students, setStudents] = useState([]);
const [search, setSearch] = useState('')
const [filteredStudents, setFilteredStudents] = useState([])
const [filteredTags, setFilteredTags] = useState([])
const [open, setOpen] = useState([])
const [tags, setTags] = useState([])
const [searchTags, setSearchTags] = useState('')
useEffect(() => {
fetch("https://www.hatchways.io/api/assessment/students")
.then(res => res.json())
.then(
(result) => {
setIsLoaded(true);
setStudents(result.students);
},
(error) => {
setIsLoaded(true);
setError(error);
}
)
}, [])
useEffect(() => {
setFilteredStudents(
students.filter(s => {
return s.firstName.toLowerCase().includes(search.toLowerCase())
|| s.lastName.toLowerCase().includes(search.toLowerCase());
})
)
}, [search, students]);
useEffect(() => {
setFilteredTags(
tags.filter(tag => {
console.dir(tag.includes(searchTags))
})
)
}, [ searchTags,students])
// const add = (id) => {
// if(tags.includes(id)) {
// setTags(tags.filter(sid => sid !== id))
// } else {
// let newTags = [...tags]
// newTags.push(id)
// setTags(newTags)
// }
// }
const addTags = event => {
if (event.key === 'Enter' && event.target.value !== '') {
setTags([...tags, event.target.value])
event.target.value = ''
}
}
// const inputKeyDown = event => {
// const val = event.target.value
// if (event.key === 'Enter' && val) {
// if (
// tags.find(tag => tag.toLowerCase() === val.toLowerCase())
// ) {
// return
// }
// setTags(...tags, val)
// event.target.value = ''
// }
// }
const toggleOpen= (id) => {
if (open.includes(id)) {
setOpen(open.filter(sid => sid !== id))
} else {
let newOpen = [...open]
newOpen.push(id)
setOpen(newOpen)
}
}
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<div >
<ul >
<form >
<input
className='input'
type="text"
placeholder="Search by name"
id="name-input"
onChange={e => setSearch(e.target.value)}
/>
<input
className='input'
type="text"
placeholder="Search by tags"
id="tag-input"
onChange={e => setSearchTags(e.target.value)}
/>
</form>
{filteredStudents.map(student => (
<li className="studentProfile" key={student.id}>
<img className='image' src={student.pic} />
<div className="text">
<h1 className='name'>{student.firstName} {student.lastName}</h1>
<div className='info'>
<p>Email: {student.email}</p>
<p>Company: {student.company}</p>
<p>Skill: {student.skill}</p>
<p>Average: {(student.grades.reduce((a, b) => parseInt(b) + a, 0))
/ (student.grades.map((grade) => grade).length)}%
</p>
<div >
<ul>
{tags.map((tag, index) => (
<li className='li' key={index}>
<span className='tag'>{tag}</span>
</li>
))}
</ul>
<input
className="tags-input"
type="text"
onKeyUp={(event) => addTags(event)}
placeholder="Press enter to add tags"
/>
</div>
{open.includes(student.id) ? (
<ul>
{student.grades.map((grade, index) => <li className='li' key={grade.id}>Test {index + 1}: {grade}%</li>)}
</ul>) : null}
</div>
</div>
<div className='button-flex'>
<button className="expand-btn" onClick={() => toggleOpen(student.id)}>{open.includes(student.id) ? '-' : '+'}</button>
</div>
</li>
))
}
</ul >
</div>
);
}
}
export default StudentProfile;
You're not adding a tag to a student. You're updating a tags array that isnt linked to your students.
What you could do : turn tags into an array of objects { student : student.id, tag : tagValue} , or into a map

Button returns correct values, but is not displayed to the screen once I press it

I'm doing this fullstack course to learn about web dev: https://fullstackopen.com/en/part2/getting_data_from_server
And I have a problem with section 2.13*.
I am able to display a list of the countries after filtering with the button. Pressing the button returns the correct values from the countries arrays as seen with the console.log(country), but it doesn't to the screen.
My guess is that I can't return a div item within another item, but I am pretty sure that works in normal cases, so the fact that I'm returning the item to a different return statement might be the issue?
How can I fix this? I know my code is messy and a refactor might make things simpler, but it is currently beyond me right now since I find it easier to refactor working code.
In the DisplayCountries component, I've tried apply a map to countries that fit the filter input and prints it into a div item. Now when I add a button beside it, it displays correctly, but pressing it does not yield what I expect.
Is the correct approach here to use a useState with the button, so that each button click will rerender the screen? How would I go about doing this if so?
After pressing the button, the detailed information of the country should display such as in 2.12* from the linked website.
import { useState, useEffect } from 'react'
import axios from 'axios'
//feed array of countries
const printLanguages = (languages) => {
// console.log('map', languages.map(language => language.name))
return languages.map(language => <li key={language.name}>{language.name}</li>)
}
const displayCountryView = (country) => {
console.log(country)
return (
<div>
<h1>{country.name}</h1>
<p>capital {country.capital}</p>
<p>population {country.population}</p>
<h2>languages</h2>
<ul>
{printLanguages(country.languages)}
</ul>
<img src={country.flag} height="100" width="100"></img>
</div>
)
}
const DisplayCountries = ({ countries, searchValue }) => {
const displayFilter = filteredCountries(countries, searchValue)
// console.log('current search', searchValue)
if (displayFilter.length >= 10) {
return <p>Too many matches, specify another filter</p>
} else if (isFiltered(searchValue)) {
if (displayFilter.length > 1 && displayFilter.length < 10) {
console.log('new level')
return displayFilter.map(country => <div key={country.name}>{country.name}{showButton(country)}</div>)
} else if (displayFilter.length === 1) {
// console.log('suh')
// return displayFilter.map(country => <p key={country.name}>{country.name}</p>)
const country = displayFilter
return displayCountryView(country[0])
// console.log(country)
// console.log('country.name', country[0])
// console.log(country[0].languages)
// console.log(printLanguages(country[0].languages))
// return (
// <div>
// <h1>{country[0].name}</h1>
// <p>capital {country[0].capital}</p>
// <p>population {country[0].population}</p>
// <h2>languages</h2>
// <ul>
// {printLanguages(country[0].languages)}
// </ul>
// <img src={country[0].flag} height="100" width="100"></img>
// </div>
// )
}
} else {
return <p>empty</p>
}
}
const showButton = (country) => {
return <button type="button" onClick={() => displayCountryView(country)}>show</button>
}
const filteredCountries = (countries, searchValue) => {
const showCountries = (!isFiltered(searchValue))
? [{ name: "hi" }]
: countries.filter(country => country.name.toLowerCase().includes(searchValue.toLowerCase()))
// const countryMap = countries.map(country => country.name.toLowerCase())
// console.log(countryMap)
// return countryMap
return showCountries
}
function isFiltered(value) {
if (value === '') {
return false
} else {
return true
}
}
const Filter = ({ search, onChange }) => {
return (
<form >
<div>
find countries <input value={search} onChange={onChange} />
</div>
</form>
)
}
const App = () => {
const [countries, setCountries] = useState([])
const [search, setNewSearch] = useState('')
const [showCountry, setShowCountry] = useState('false')
useEffect(() => {
// console.log('effect')
axios
.get('https://restcountries.eu/rest/v2/all')
.then(response => {
// console.log('promise fulfilled')
setCountries(response.data)
})
}, [])
// const countryNames = countries.map(country => country.name)
// console.log('name', countryNames)
const handleSearchChange = (event) => {
setNewSearch(event.target.value)
}
// const fil = countries.filter(country => country.name==='Afg')
// console.log(countries[0])
// console.log('filtered:',fil)
// console.log(countries[0])
// console.log('render', countries.length, 'persons')
return (
<div>
<Filter search={search} onChange={handleSearchChange} />
<form>
<div>
<DisplayCountries countries={countries} searchValue={search} />
</div>
</form>
</div>
)
}
export default App;

Categories