Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React useState() array not updating

I have created new custom selectbox using React. There is pre-populated array which I am loading on component load (using useEffect). When user search for any non existing country there would be a option to add. So I used useState hooked. Code is as under :-

const [countries, setCountries] = useState([]);

Countries list :-

[
  {"value": 1, "label": "Singapore"},
  {"value": 2, "label": "Malaysia"},
  {"value": 3, "label": "Indonesia"},
  {"value": 4, "label": "Phillipines"},
  {"value": 5, "label": "Thailand"},
  {"value": 6, "label": "India"},
  {"value": 7, "label": "Australia"},
  {"value": 8, "label": "Pakistan"}
]


const handleHTTPRequest = () => {
    service.getData() 
    .then(res => {
      setCountries(res);
    })
    .catch((err) => console.error(err))
  }

  useEffect(() => {
    handleHTTPRequest()
  })

I am checking the searched country in the array and if not exists I simply add in array

const addCountry = (country) => {
    let isRecordExist = countries.filter(c => c.label === country).length > 0 ? true : false;
    const obj = {
      value: countries.length + 1,
      label: country
    }
    let updatedVal = [...countries, obj]
    
    setSelectedCountry(country)

    if (!isRecordExist) {
      **setCountries**(updatedVal) // updating array
    }
  }

The problem is its not updating although I can see data in updatedVal.

Entire code is here :-

https://github.com/ananddeepsingh/react-selectbox

like image 956
Anand Deep Singh Avatar asked Dec 10 '25 07:12

Anand Deep Singh


2 Answers

The problem appears to be that you're passing to useState() the array (updatedVal) that has its reference unchanged, thus it appears to React that your data hasn't been modified and it bails out without updating your state.

Try drop that unnecessary variable and do directly setCountries([...countries, obj])

Another minor fix about your code I may suggest: you may use Array.prototype.every() to make sure that every existing item has different label. It has two advantages over .filter() - it will stop looping right upon hitting duplicate (if one exists) and won't proceed till the end of array (as .filter() does), thus won't slow down unnecessarily re-render and it returns boolean, so you won't actually need extra variable for that.

Following is a quick demo of that approach:

const { useState, useEffect } = React,
      { render } = ReactDOM,
      rootNode = document.getElementById('root')
      
const CountryList = () => {
  const [countries, setCountries] = useState([])
  
  useEffect(() => {
    fetch('https://run.mocky.io/v3/40a13c3b-436e-418c-85e3-d3884666ca05')
      .then(res => res.json())
      .then(data => setCountries(data))
  }, [])
  
  const addCountry = e => {
    e.preventDefault()
    const countryName = new FormData(e.target).get('label')
    if(countries.every(({label}) => label != countryName))
      setCountries([
        ...countries,
        {
          label: countryName,
          value: countries.length+1
        }
      ])
    e.target.reset()
  }
  
  return !!countries.length && (
    <div>
      <ul>
        {
          countries.map(({value, label}) => (
            <li key={value}>{label}</li>
          ))
        }
      </ul>
      <form onSubmit={addCountry}>
        <input name="label" />
        <input type="submit" value="Add country" />
      </form>
    </div>
  )
}

render (
  <CountryList />,
  rootNode
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>
like image 71
Yevgen Gorbunkov Avatar answered Dec 12 '25 21:12

Yevgen Gorbunkov


I have found the root problem. In your Select Box component you have:

 const [defaultValueoptions, setdefaultValueoptions] = useState(options);

It should be:

 const [defaultValueoptions, setdefaultValueoptions] = useState([]);

  useEffect(() => {
    setdefaultValueoptions(options);
  }, [options]);
like image 44
Talmacel Marian Silviu Avatar answered Dec 12 '25 21:12

Talmacel Marian Silviu