TransWikia.com

React State value not updated in Arrow functional component

Stack Overflow Asked on January 6, 2021

React state value not updated in the console but it is updated in the view.

This is my entire code

import React, { useEffect, useState } from 'react';

const Add = (props) => {
    console.log("a = ", props.a)
    console.log("b = ", props.b)
    const c = props.a+props.b;
    return (
        <div>
            <p><b>{props.a} + {props.b} = <span style={{'color': 'green'}}>{c}</span></b></p>
        </div>
    )
}

// export default React.memo(Add);

const AddMemo = React.memo(Add);

const MemoDemo = (props) => {

    const [a, setA] = useState(10)
    const [b, setB] = useState(10)
    const [i, setI] = useState(0);

    
    useEffect(() => {
        init()
        return () => {
            console.log("unmounting...")
        }
    }, [])

    const init = () => {
        console.log("init", i)
        setInterval(()=>{
            console.log("i = ", i)
            if(i == 3){
                setA(5)
                setB(5)
            }else{
                setA(10)
                setB(10)
            }
            setI(prevI => prevI+1)
        }, 2000)
    }

    return (
        <div>
            <h2>React Memo - demo</h2>
            <p>Function returns previously stored output or cached output. if inputs are same and output should same then no need to recalculation</p>
    <b>I= {i}</b>
            <AddMemo a={a} b={b}/>
        </div>    
    );
}   

export default MemoDemo;

Please check this imageenter image description here

Anyone please explain why this working like this and how to fix this

2 Answers

The problem is as you initialized the setInterval once so it would reference to the initial value i all the time. Meanwhile, React always reference to the latest one which always reflect the latest value on the UI while your interval is always referencing the old one. So the solution is quite simple, just kill the interval each time your i has changed so it will reference the updated value:

React.useEffect(() => {
    // re-create the interval to ref the updated value
    const id = init();
    return () => {
      // kill this after value changed
      clearInterval(id);
    };
     // watch the `i` to create the interval
  }, [i]);

  const init = () => {
    console.log("init", i);
    // return intervalID to kill
    return setInterval(() => {
      // ... 
    });
  };

Correct answer by tmhao2005 on January 6, 2021

In callback passed to setInterval you have a closure on the value of i=0.

For fixing it you can use a reference, log the value in the functional update or use useEffect:

// Recommended
useEffect(() => {
  console.log(i);
}, [i])


const counterRef = useRef(i);
setInterval(()=> {
  // or
  setI(prevI => {
    console.log(prevI+1);
    return prevI+1;
  })

  // or 
  conosole.log(counterRef.current);
}, 2000);

Answered by Dennis Vash on January 6, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP