Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

React useState hook not updating correctly

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading