skip to Main Content
import React, { useState, useEffect } from 'react'

function Counter() {
  const [count, setCount] = useState(0)

  const incCount = () => {
    console.log("count ", count)

    // setCount(count + 1)
    setCount(x => x + 1)
  }

  useEffect(() => {
    console.log('use effect called')
    const intervalId = setInterval(() => incCount(), 1000)
    return() => {
      // clearInterval(intervalId);
      console.log("clear interval called")
    }
  }, [])

  return (
    <div>
      {count}
    </div>
  )
}

export default Counter

In display count is increasing as expected but in console every time it shows 0 only. incCount is executing after every one second and in console count 0 is printing is whereas on screen count is increasing by 1 every time as expected.

2

Answers


  1. In your React component, the incCount function is using the count variable from its initial render, which remains constant at 0 in the console log. The setCount call updates the state correctly, so the displayed count increments as expected, but since the count variable inside incCount doesn’t update between renders, it logs 0 every time.

    To see the updated count in the console each time incCount is called, you should log the count state within the setCount callback function. Here’s how you can modify your code to achieve that:

    import React, { useState, useEffect } from 'react';
    
    function Counter() {
      const [count, setCount] = useState(0);
    
      const incCount = () => {
        setCount((prevCount) => {
          const newCount = prevCount + 1;
          console.log("count ", newCount);
          return newCount;
        });
      };
    
      useEffect(() => {
        console.log('use effect called');
        const intervalId = setInterval(incCount, 1000);
        return () => {
          clearInterval(intervalId);
          console.log("clear interval called");
        };
      }, []);
    
      return (
        <div>
          {count}
        </div>
      );
    }
    
    export default Counter;

    In this updated code, the console.log statement is placed inside the setCount callback, which receives the most recent state value (prevCount). This ensures that the console logs the updated count each time incCount is called.

    Login or Signup to reply.
  2. The useEffect hook is running as expected, there’s no issue there.

    The issue you have is that of a stale Javascript Closure over the count state in the incCount function scope that is passed to the setInterval callback. setInterval gets that initial instance "copy" of incCount that it just calls repeatedly each interval. I never gets a new copy with the updated count state value closed over in scope.

    Methods to address:

    1. Use a useEffect hook to console log the count state when it updates. This is the simplest and most trivial.

      import React, { useState, useEffect } from 'react';
      
      function Counter() {
        const [count, setCount] = useState(0);
      
        const incCount = () => {
          setCount(count => count + 1);
        };
      
        useEffect(() => {
          const intervalId = setInterval(incCount, 1000);
          return () => {
            clearInterval(intervalId);
          };
        }, []);
      
        useEffect(() => {
          console.log("count ", count);
        }, [count]);
      
        return <div>{count}</div>;
      }
      
      export default Counter;
      
    2. Use a React ref to cache the count state that can be referenced from the closure.

      import React, { useState, useEffect, useRef } from 'react';
      
      function Counter() {
        const [count, setCount] = useState(0);
        const countRef = useRef(count);
      
        const incCount = () => {
          console.log("count ", countRef.current);
          setCount(count => count + 1);
        };
      
        useEffect(() => {
          const intervalId = setInterval(incCount, 1000);
          return () => {
            clearInterval(intervalId);
          };
        }, []);
      
        useEffect(() => {
          countRef.current = count;
        }, [count]);
      
        return <div>{count}</div>;
      }
      
      export default Counter;
      

    Note

    While you could console.log the current count state value in the setCount state updater function

    setCount(count => {
      const nextCount = count + 1;
      console.log("count ", nextCount);
      return nextCount;
    });
    

    and there’d be no adverse reaction since console.log is rather benign, you should be aware this is not recommended as the state updater functions are to be considered pure synchronous functions, and technically speaking logging the value is a side-effect and should be avoided.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search