So, here is our Scenario:
This is a small application that takes in userInput and creates a search query for it on google.
On enter or on clicking "Search" button the user is re-directed to google to find the results of their query.
Here is the component responsible for that:
import {useState} from 'react'
function Search() {
///searchQuery initialised
const [userQuery, setUserQuery] = useState('');
//open searchQuery on new google.com window
const searchQuery = () =>{
window.open(`https://google.com/search?q=${userQuery}`, '_blank');
}
//search on Enter key
const handleKeyPress =(e) => {
if(e.key==="Enter"){
searchQuery();
}
}
const updateUserQuery =(e) =>{
setUserQuery(e.target.value);
console.log('userQueryInsideCBFunction:', userQuery)
}
return (
<div className="Search_bar">
<h1>Search the Web</h1>
<div className="form">
<input value={userQuery}
onChange={updateUserQuery}
onKeyPress ={handleKeyPress}/>
<button onClick={searchQuery}>search</button>
</div>
</div>
)
}
export default Search
NOW HERE IS OUR PROBLEM:
Oh -oh! Our console.log inside the updateUserQuery function "appears" to log ONE CHARACTER BEHIND.!!
However, our State is updated correctly as per the input values, and we can confirm this by adding a console.log for the state variable,
const updateUserQuery =(e) =>{
setUserQuery(e.target.value);
console.log('userQueryInsideCBFunction:', userQuery);
}
///add this to confirm state is indeed updated
console.log('userQueryOutsideCBFunction:', userQuery)
return (
....
}
export default Search
We can see that userQueryOutsideCBFunction is up-to- date:
SO, WHAT'S HAPPENING AND HOW DO WE FIX THIS?
Our State update is dependent on the result of an asynchronous operation. --
which in our case is The ,
setUserQuery(e.target.value);
inside the updateUserQuery function
The updateUserQuery function is called every time the input field is updated, but the userQuery state is not updated until after the function has finished executing.
This means that the userQuery state will be one character behind the current input value, because the input value is updated before the userQuery state is updated.
In this case, the reason the state is "behind" the input value is because the updateUserQuery function is called synchronously in response to the onChange event, but the setUserQuery function is called asynchronously after the function has finished executing.
This means that the state update is deferred until after the function has completed, causing the state to be temporarily out of sync with the input value.
Layman's explanation
LET'S USE "HI" AS OUR EXAMPLE INPUT FIELD.
The console of userQuery inside the updateUserQuery function will be...you guessed it, "h" instead of "hi"
Why?
The State is NOT updated until the updateUserQuery function is finished,so that means...console.logging userQuery in the function at first will log an empty string ""(which is the default state variable in our case) - So this is our FIRST STATE
SO THIS IS WHAT IS HAPPENING INSIDE THAT FUNCTION:
So at first our console will print ""///empty initial value, and the e.target.value at this point is at,"h"
Then when the user types again, the e.target.value now adds the i, and gets to "hi".At this stage our state will only be at "h"
SOLUTIONS:
There are various ways to address this issue, depending on your specific requirements and the context in which it occurs.
For example, you could try using the setState callback to update the state synchronously, or use the useReducer hook instead of useState to manage state updates more explicitly.
Quick fix:
To log the new input value instead of the current userQuery state, you could update the console.log statement to use the e.target.value property instead of userQuery. This property contains the current value of the input field, which should match the new input value exactly.
Here is an example of how you could update the console.log statement to log the new input value:
const updateUserQuery =(e) =>{
console.log('userQuery', e.target.value);
setUserQuery(e.target.value);
}
With this change, the console.log statement will log the new input value each time the input field is updated, rather than logging the current userQuery state.
Top comments (2)
Tip: For code highlighting use
Thanks ...