Search code examples
javascriptreactjsreact-hooksnext.jsuse-effect

React: missing dependency warning within interval in effect


I have a react page built with React and Next.js that looks like this.

import Head from 'next/head';
import { useEffect, useState } from 'react';
import Header from '../components/Header';

export default function Home() {
    const ticksPerSecond = 10;

    const [points, setPoints] = useState(10);
    const [pointsPerSecond, setPointsPerSecond] = useState(2);

    useEffect(() => {
        setInterval(() => {
            setPoints((points) => points + pointsPerSecond / ticksPerSecond);
        }, 1000 / ticksPerSecond);
    }, []);

    return (
        <>
            <Header points={points} pointsPerSecond={pointsPerSecond} />
        </>
    );
}

This code works as intended. Every second, points will increase by pointsPerSecond, and the state will be updated 10 times a second (as determined by ticksPerSecond).

My issue comes from eslint which warns me:

React Hook useEffect has a missing dependency: 'pointsPerSecond'. Either include it or remove the dependency array. You can also replace multiple useState variables with useReducer if 'setPoints' needs the current value of 'pointsPerSecond'.eslintreact-hooks/exhaustive-deps

Having read that warning, I have tried many different solutions to appease the eslint gods in the sky, but the best I can do it replace the warnings with different warnings. Specifically, I have tried the solutions from this question which looks like the same problem. However, none of those solutions suppress the warning.


Solution

  • I recommend adding pointsPerSecond to the dependency array and then making sure to return a cleanup function from your effect to clear the existing interval. This assumes you want to stop any existing interval and start a new one whenever pointsPerSecond changes. If you don't include pointsPerSecond in the dependency array, you'll be working with a stale version of that dependency.

    import Head from 'next/head';
    import { useEffect, useState } from 'react';
    import Header from '../components/Header';
    
    const ticksPerSecond = 10;
    
    export default function Home() {
        const [points, setPoints] = useState(10);
        const [pointsPerSecond, setPointsPerSecond] = useState(2);
    
        useEffect(() => {
            const interval = setInterval(() => {
                setPoints((points) => points + pointsPerSecond / ticksPerSecond);
            }, 1000 / ticksPerSecond);
    
            return () => { clearInterval(interval) }
        }, [pointsPerSecond]);
    
        return (
            <>
                <Header points={points} pointsPerSecond={pointsPerSecond} />
            </>
        );
    }
    

    There are a couple reasons I almost always recommend against silencing linting errors for hook dependency arrays:

    • It's almost always the case that you want your hook to have the most current value.
    • If you silence the lint warnings for the dependency array, then you won't get these warnings for future dependencies for which you might really want to have the warnings.