0

I have this simple onChange function, but I have problem. When I change my input first time and for example enter "1" my variable inputOne is returns empty string:

const [inputOne, setInputOne] = useState('');
const onChange = (e) => {
 setInputOne(e.target.value);

  console.log(e.target.value); // first changing of input returns 1
  console.log(inputOne); // first changing of input returns EMPTY string

}
   <div className="container">
          <input type="number" name='input1' value={inputOne} onChange={onChange} />
   </div>

but when I change this input again and add one more "1"(in total 11) my console is:

console.log(e.target.value); // returns 11
console.log(inputOne); // returns 1

Why it's happening with my variable inputOne?

New code:

const [inputOne, setInputOne] = useState('');

useEffect(() => {
 console.log(inputOne);
}, [inputOne])

const onChange = (e) => {

  setInputOne(e.target.value);
  console.log(e.target.value);
  setTimeout(() => { 
  
  if(e.target.value){
  const filteredpost = posts[0].filter(mfo => mfo.minPrice <= Number(inputOne));
  setPostsToShow(filteredpost.slice(0, 20));
  setPost(filteredpost);
  }else{
    const filteredpost = posts[0];
    setPostsToShow(filteredpost.slice(0, 20));
    setPost(filteredpost);
  }}, 1000);
}
2
  • 1
    why is that surprising? it seems the onChange function needs to complete first before the component reactively reruns, and only at the point the change you just made will be reflected in the variable. Until then it still has the old value ('' at first, and then '1') Commented Apr 10, 2021 at 21:22
  • no, this onChange function is completed and 2 the same value return different results. it looks like delay with one symbol for my variable. Commented Apr 10, 2021 at 21:24

2 Answers 2

2

setState is an async function.

In your case, setInputOne queues the change and returns a Promise, that will not be resolved until the next tick (or even later, if reacts thinks it is worth it to gain some performance).

So the timeline is like this:

  • Type into input
  • Trigger onChange
  • setInputOne (queue the change)
  • console.log (the value that is queued)
  • console.log (the variable that is queued)
  • next tick and consequently the change of the variable.

You can see this with the useEffect hook:

useEffect(() => {

console.log(`tell me when inputOne changes`);

}, [inputOne])

UPDATE

inputOne will never be your updated value inside the onChange function. The onChange function stores the last value until re-render.

Pass your setTimeout to the useEffect OR change inputOne to e.target.value since they will always be the same.

Sign up to request clarification or add additional context in comments.

7 Comments

Thanks! Console log is working for me correctly now. But I should use my variable "inputOne" inside my onChange function, I updated question and added my full function, please check.
Are you using setTimeout because you are waiting for the inputOne value to change, or are you using it because you actually need it?
it needs for me. I do filtering and I need delay 1 sec for good UX.
yes, I can use e.target.value, but if I will have 3 inputs with different values, how I can use the same onchange function?
What do you mean? Do they need to exchange information or are they independent?
|
2

State update in Reactjs is an asynchronous process, therefore it won't be reflected immediately in the next line due to it's asynchronous nature.

If you want to monitor the state whenever its updated, you can use useEffect hook, and place inside its the dependency array the piece of state you want to track.

In your case:

useEffect(() => {
 console.log(inputOne);
}, [inputOne])

This will be triggered, every time inputOne changes. If you want to use the value from the inputOne to call another function you should implement that logic inside the useEffect, instead of doing it inside the function onChange which updates the inputOne state.

useEffect(() => {
    if(inputOne){
        const filteredpost = posts[0].filter(mfo => mfo.minPrice <= Number(inputOne));
        setPostsToShow(filteredpost.slice(0, 20));
        setPost(filteredpost);
     }
        else
     {
          const filteredpost = posts[0];
          setPostsToShow(filteredpost.slice(0, 20));
          setPost(filteredpost);
    }
}, [inputOne]);

Get rid of the timeout. It's unnecessary.

3 Comments

Thanks! Console log is working for me correctly now. But I should use my variable "inputOne" inside my onChange function, I updated question and added my full function, please check.
Updated my answer.
hmm, thanks. but how should look my input then?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.