useEffect exhaustive-deps rule is confusing

Viewed 246

I have found a few questions related to this on stackoverflow, but nothing that satisfies a use-case which I have very regularly while using useEffect. Let's say I have some code like this:

const ApiViewer = (props) => {
    const [result, setResult] = useState('')

    useEffect(async () => {
        setResult(await callApi(props.valueThatWillChangeButIOnlyCareAboutItsInitialValue))
    }, [])

    return <div>{result}</div>
} 

The exhaustive-deps rule will throw, asking me to put props.valueThatWillChangeButIOnlyCareAboutItsInitialValue in the dependency array. I don't want to do this as I only want the initial value. I may use that prop in a separate effect somewhere.

I can't think of another way to write this either.

I also have the problem that other people seem to have, i.e. using functions/etc that I know will never change. I don't want to add them to this array, seems very dumb.

Dan Abramov said that Usually disabling it is a mistake and will significantly bite you later. I've been using it this way for years and I've seen no issues until this rule started popping up in a new project.

Am I using React completely wrong?

Edit: I've read through this thread more thoroughly, and have concluded that Dan Abramov really cares about programmers because he wants them to have a job for life! I am removing this rule from my projects now.

2 Answers

Don't worry, you are doing it right! Exhaustive dep is just a warning thrown by eslint, and just because you got the warning it doesn't mean you are doing it completely wrong. If your code demands something to be run only on the initial load of the component, what you are doing it completely right.

Although I would suggest you don't remove this rule completely and to just suppress them where ever necessary.

EDIT: The reason I said not to completely disable the rule was because, other hooks like useCallback and useMemo also works based on dependency list. And if you failed to add your dependency list for those hooks, it can completely backfire on you.

Having the warning is always good, because you are not the only one who's gonna be working on a project, this can also help a new junior developer that you hire tomorrow.

If you want to ignore updates to a given prop then you can clarify that by using a useRef. When referencing the ref inside a useEffect, you won't get any warnings because the base object returned from useRef never changes. Using an initial prefix or something similar will make this obvious to the next developer as well.

const ApiViewer = (props) => {
    const [result, setResult] = useState('')
    const initialValueThatWillChangeRef = useRef(props.valueThatWillChange);

    useEffect(async () => {
        setResult(await callApi(initialValueThatWillChangeRef.current))
    }, [])

    return <div>{result}</div>
} 

You can certainly construct examples where everything works as expected by ignoring the exhaustive deps rule, but it'll work until it doesn't. If you convince yourself that the rule is not a rule but rather a suggestion, then you will eventually run into a nuanced case that breaks for reasons that are difficult to debug.

Treating exhaustive deps as the true rule that it is will ensure components update as expected.

Related