Reputation: 567
import React, { useState, useEffect } from "react";
import axios from "axios";
const App = () => {
let [countries, setCountries] = useState([]);
const [newCountry, newStuff] = useState("");
const hook = () => {
//console.log("effect");
axios.get("https://restcountries.eu/rest/v2/all").then((response) => {
console.log("promise fulfilled");
setCountries(response.data);
//console.log(response.data);
});
};
const filter = (event) => {
newStuff(event.target.value);
if (event.target.value === undefined) {
return
} else {
let value = event.target.value;
console.log(value);
countries = countries.filter((country) => country.name.startsWith(value));
setCountries(countries);
console.log(countries);
}
};
useEffect(hook, []);
return (
<div>
<p>find countries</p>
<input value={newCountry} onChange={filter} />
<ul>
{countries.map((country) => (
<li key={country.name.length}>{country.name}</li>
))}
</ul>
</div>
);
};
export default App;
So I have a search bar so that when you enter a few characters it will update the state and show the countries that start with the respective first characters. However, nothing is being shown when I enter input into my search bar. Also, my filter function, when I console.log my countries array which is supposed to have the countries that start with the characters I entered, it's always an empty array.
Upvotes: 0
Views: 1231
Reputation: 41
You need some changes in order to make this work:
Use two states for countries, one for the list you get in the initial render and another for the current filter countries.
const [countriesStore, setCountriesStore] = useState([]); // this only change in the first render
const [countries, setCountries] = useState([]); // use this to print the list
I recomed to use any tool to manage the state and create a model for the countries ther you can make the side effect there and create an action that update the countries store. I'm using Easy Peasy in my current project and it goes very well.
Take care of the filter method because startsWith method is not case-insensitive. You need a regular expression or turn the current country value to lower case. I recommend to use includes method to match seconds names like island in the search.
const filterCountries = countriesStore.filter(country => {
return country.name.toLowerCase().includes(value);
});
Remove the if condition in the filter in order to include the delete action in the search and get the full list again if everything is removed.
Just in the case, empty the search string state in the first render
useEffect(() => { hook(); setSearchString(""); }, []);
Replace the length in the list key. You can use the name and trim to remove space.
<li key={country.name.trim()}>{country.name}</li>
The final code look like this:
export default function App() {
const [countriesStore, setCountriesStore] = useState([]);
const [countries, setCountries] = useState([]);
const [searchString, setSearchString] = useState("");
const hook = () => {
axios.get("https://restcountries.eu/rest/v2/all").then(response => {
console.log("promise fulfilled");
setCountriesStore(response.data);
setCountries(response.data);
});
};
const filter = event => {
setSearchString(event.target.value);
let value = event.target.value;
const filterCountries = countriesStore.filter(country => {
return country.name.toLowerCase().includes(value);
});
setCountries(filterCountries);
};
useEffect(() => {
hook();
setSearchString("");
}, []);
return (
<div>
<p>find countries</p>
<input value={searchString} onChange={filter} />
<ul>
{countries.map(country => (
<li key={country.name.trim()}>{country.name}</li>
))}
</ul>
</div>
);
}
Upvotes: 1
Reputation: 8847
You need to wrap your hook into async useCallback:
const hook = useCallback(async () => {
const {data} = await axios.get("https://restcountries.eu/rest/v2/all");
setCountries(data);
}, []);
you are not able to mutate state countries. Use immutable way to update your state:
const filter = (event) => {
newStuff(event.target.value);
if (event.target.value === undefined) {
return
} else {
let value = event.target.value;
setCountries(countries.filter((country) => country.name.startsWith(value)));
}
};
And useState
is asynchronous function. You will not see result immediately. Just try to console.log outside of any function.
Upvotes: 0