Search code examples
reactjssetintervaluse-effect

React : Access multiple states in setInterval() in useEffect()


I have this code which updates the state count every 1 seconds.
How can I access the value of the state object in setInterval() ?

    import React, {useState, useEffect, useCallback} from 'react';
    import axios from 'axios';
    
    export default function Timer({objectId}) {
    
        const [object, setObject] = useState({increment: 1});
        const [count, setCount] = useState(0);
    
        useEffect(() => {
            callAPI(); // update state.object.increment
    
            const timer = setInterval(() => {
                setCount(count => count + object.increment); // update state.count with state.object.increment
            }, 1000);
    
            return () => clearTimeout(timer); // Help to eliminate the potential of stacking timeouts and causing an error
        }, [objectId]); // ensure this calls only once the API
    
    
        const callAPI = async () => {
            return await axios
                .get(`/get-object/${objectId}`)
                .then(response => {
                    setObject(response.data);
                })
        };
    
        return (
            <div>{count}</div>
        )
    }

The only solution I found is this :

// Only this seems to work
const timer = setInterval(() => {
    let increment = null;
    setObject(object => { increment=object.increment; return object;}); // huge hack to get the value of the 2nd state
    setCount(count => count + increment);
}, 1000);

Solution

  • In your interval you have closures on object.increment, you should use useRef instead:

    const objectRef = useRef({ increment: 1 });
    
    useEffect(() => {
      const callAPI = async () => {
        return await axios.get(`/get-object/${objectId}`).then((response) => {
          objectRef.current.increment = response.data;
        });
      };
      callAPI();
    
      const timer = setInterval(() => {
        setCount((count) => count + objectRef.current);
      }, 1000);
    
      return () => {
        clearTimeout(timer);
      };
    }, [objectId]);