I’m a beginner learning React and ran into a problem that I can’t figure out. Basically I have an input field that filters out results from a list, and a different list should be rendered depending on the filter that is typed in. The problem is that when the results are filtered and rendered, basically the last letter of the input value is ignored. Example below.
If I type the character "s", nothing happens: Image. What should happen instead is that a paragraph tag should be rendered with the content "Too many matches, specify another filter".
Then, if I type in the letter "w", it renders the paragraph tag that should’ve been rendered before: Image. What should’ve happened instead is that a list of countries with names containing the substring "sw" should’ve been rendered.
Then, if I type in the letter "e", it renders the list that should’ve been rendered when I typed in the previous character: Image. As you can see, the names of the countries in the list contain the substring "sw", basically ignoring the last character that was inputted.
My code:
main App component:
import axios from "axios";
import Filter from "./Filter";
import "./styles.css";
import Content from "./Content";
const App = () => {
const [countries, setCountries] = useState([])
const [allCountries, setAllCountries] = useState([])
const [newFilter, setNewFilter] = useState("")
useEffect(() => {
axios
.get('https://restcountries.com/v3.1/all')
.then(response => {
setAllCountries(response.data)
})
}, [])
const handleFilterChange = (event) => {
setNewFilter(event.target.value);
if (newFilter) {
const regex = new RegExp(newFilter, "i");
const filteredCountries = () => allCountries.filter(country => country.name.common.match(regex));
setCountries(filteredCountries)
}
}
return (
<div>
<Filter value={newFilter} onChange={handleFilterChange} />
<Content countries={countries} setCountries={setCountries} />
</div>
)
}
export default App
Filter (input field) component:
import React from "react";
const Filter = (props) => {
return (
<input value={props.value} onChange={props.onChange} />
);
}
export default Filter;
Content component:
import React from "react";
const Content = ({countries, setCountries}) =>{
if (countries.length > 10){
return <p>Too many matches, specify another filter</p>;
} else if ((countries.length > 2 && countries.length < 10) || countries.length === 0){
return <ul>{countries.map((country, i) => <li key={i}>{country.name.common}</li>)}</ul>;
} else {
return <p>{countries[0].name.common}</p>;
}
}
export default Content;
Help would be appreciated!
>Solution :
import "./styles.css";
import Content from "./Content";
const App = () => {
const [countries, setCountries] = useState([])
const [allCountries, setAllCountries] = useState([])
const [newFilter, setNewFilter] = useState("")
useEffect(() => {
axios
.get('https://restcountries.com/v3.1/all')
.then(response => {
setAllCountries(response.data)
})
}, [])
const handleFilterChange = (event) => {
setNewFilter(event.target.value);
if (newFilter) { // at this point, the newFilter is not set as a new value yet. So you'd better to use event.target.value instead of newFilter.
const regex = new RegExp(newFilter, "i");
const filteredCountries = () => allCountries.filter(country => country.name.common.match(regex));
setCountries(filteredCountries)
}
}
return (
<div>
<Filter value={newFilter} onChange={handleFilterChange} />
<Content countries={countries} setCountries={setCountries} />
</div>
)
}
export default App
Or you can use useEffect hook.
import "./styles.css";
import Content from "./Content";
const App = () => {
const [countries, setCountries] = useState([])
const [allCountries, setAllCountries] = useState([])
const [newFilter, setNewFilter] = useState("")
useEffect(() => {
axios
.get('https://restcountries.com/v3.1/all')
.then(response => {
setAllCountries(response.data)
})
}, [])
useEffect(() => {
if (newFilter) {
const regex = new RegExp(newFilter, "i");
const filteredCountries = () => allCountries.filter(country => country.name.common.match(regex));
setCountries(filteredCountries)
}
}, [newFilter])
const handleFilterChange = (event) => {
setNewFilter(event.target.value);
}
return (
<div>
<Filter value={newFilter} onChange={handleFilterChange} />
<Content countries={countries} setCountries={setCountries} />
</div>
)
}
export default App
Hope this would be helpful for you.