I’m trying to make an input filtered based list that only shows results when there are 10 or less objects, but the array.length is always lagging one step behind the input.
const [countriesToShow, setCountriesToShow] = useState([])
const [newSearch, setSearch] = useState('')
const [showSearched, setShowShearched] = useState(true)
const [notificationMessage, setNotificationMessage] = useState(null)
useEffect(() => {
console.log(countriesToShow.length)
},[countriesToShow])
const handleSearchChange = (event) => {
setCountriesToShow(countries.filter(country =>
country.name.common.toLowerCase().includes(event.target.value.toLowerCase())))
setSearch(event.target.value)
if (event.target.value.trim().length === 0) {
setNotificationMessage(null)
setShowShearched(false)
}
else if (countriesToShow.length <= 10) {
setNotificationMessage(null)
setShowShearched(true)
console.log(countriesToShow.length)
}
else {
setNotificationMessage('list too long')
setShowShearched(false)
console.log(countriesToShow.length)
}
}
I managed to get the console print the right length with the help of Effect Hook but I’m stumped about how to implement the same to the ‘else if (countriesToShow.length <= 10)’ as it still lags behind the input.
2
Answers
When you call
handleSearchChange
and then callsetCountriesToShow
to udpate that state:You are triggering a re-render. The newly updated state value will only become available then, in the upcoming re-render, that’s why it’s lagging behind.
If you want to use that value below, you need to store it in a variable first:
The answer to your question
This is because whenever you call
setCountriesToShow
you aren’t updating thecountriesToShow
variable. This variable gets reinitialized on the next tick because the assignment to the result of theuseState
function is executed again when your component is run.As an analogy you could consider this code:
this is basically how react works. So the local
state
variable never gets changed. WhenmyFunction
is rerun however the localstate
value will be reinitialized to the newest valueHow to fix it
You could of course make a temporary variable like
newCountriesToShow
and assign that to the new value and then callsetCountriesToShow(newCountriesToShow)
then inside of your function usenewCountriesToShow
this could work but is an anti pattern. You shouldn’t do this unless you absolutely have to. See Danziger’s Answer for more details on this approach.What React wants you to do here is to use derived values (
useMemo
or top level code). In general try to use as fewuseState
s as possible (I’m not suggesting simply combining fields into an object when there is no other benefit but that is the exception)Any code that you want to run when
notificationMessage
orshowSearched
changes should be in auseEffect