Bug: exhaustive-deps false positives?

This issue has been created since 2022-09-07.

This is a re-opening of #22581. I had to step away from the project for a while and so I didn't respond before it was closed, my apologies. As I mentioned there I did look through the various suggestions in #14920 and nothing seemed to be relevant to my case.

The reply in my original issue helped me fix a few things, but the linter still suggests things that break the code, and I'm still unclear on a few parts of the reply.

Refresher: I have a Component, StatusBox. All it does is pop up a little overlay to tell you that, for example, something was submitted. If there's a timeout, after a timeout, it fades away, otherwise it stays until someone clicks on it.

As it stands my code now looks like:

let innerTimeout
let outerTimeout

/*
 * A box for appearing status messages
 */
const StatusBox = ({content, setContent, timeout, type}) => {
    const div = React.useRef(null)
    const [opacity, setOpacity] = React.useState(0)
    const [className, setClassName] = React.useState(type)

    const handleClick = e => {
        e.preventDefault()
        clear()
    }

    const clear = React.useCallback((e) => {
        console.debug("StatusBox: Clearing opacity, currently:", opacity)
        setOpacity(0)
        /*
         * If we remove the class now, it'll cut short the fade-out.
         * If we remove the text now, it'll disappear abruptly before the
         * fade-out.
         *
         * HOWEVER, if we DON'T clear the text, then the overlay is still
         * there, blocking the page (in the case of a bigger status), so
         * we need to clear it after the transition. So get the timing of
         * that, and add 50ms just to be safe. And while we're at it,
         * we can clear the classes...
         */
        let t = window.getComputedStyle(div.current).transitionDuration
        console.debug("StatusBox: computed transition", t)
        // that's something like 0.4s, we need to drop the 's', convert
        // to a number, and the turn into milliseconds. Trying to use a
        // string in a math equation causes JS to cast it for you
        t = t.replace('s', '') * 1000
        t += 50
        console.debug("StatusBox: Setting timeout to clear type/content in", t)
        innerTimeout = window.setTimeout(() => {
            setContent(null)
        }, t)
    }, [setContent, opacity])

    React.useEffect(() => {
        console.debug(
            `StatusBox: in useEffect, timeout ${timeout}`
        )
        // If we're setting content to null, we've already set the opacity
        // so we can return null to not re-render
        if (content == null) {
            return null
        }

        // otherwise, we need to appear!
        console.debug("StatusBox: setting opacity to 1")
        setOpacity(1)

        // if we have no timeout, we're done
        if (timeout == null) {
            return
        }

        // otherwise, set the timeout
        console.debug('StatusBox: setting timeout for', timeout)
        // nuke any timeout already there
        if (innerTimeout) {
            console.debug("clearing inner:", innerTimeout)
            window.clearTimeout(innerTimeout)
        }
        if (outerTimeout) {
            console.debug("clearing outer:", innerTimeout)
            window.clearTimeout(outerTimeout)
        }
        outerTimeout = window.setTimeout(clear, timeout)
    }, [content])

    return <div
        className={type}
        ref={div}
        id='status'
        style={{opacity:opacity}}
        onClick={handleClick}
    >{content}</div>
}

This takes into account the suggestion to move clear to be a useCallback. However, now the linter wants both opacity and setContent in the deps list of the useCallback, and then content, clear, and timeout, in the deps list of useEffect. That leads to an infinite loop in the clearing of the box and it never clears.

I think @keanemind was trying to address that with:

To fix the stale clear problem, you can put clear into the dependencies array and then have your effect clean up by cancelling the outerTimeout. Now, when clear changes, the timeout with the stale clear will be cancelled, and then the effect will run again. And by using useCallback on clear, you can have clear change only when setContent changes.

But I didn't follow that. I... do clear outerTimeout in the useEffect already. I tried moving the clear to the very top of the useEffect in case you meant I might not clear it in some cases, but that didn't fix it.

@keanemind also asked:

By the way, is there a reason you don't unmount the overlay after the animation is over, rather than setting the content to null? That part is a little odd to me.

I briefly used class components when I was learning React, but quickly found that the recommendation was to use function components, and in function components I ... don't know how to unmount things.

But also, if I unmounted it, wouldn't that <StatusBox> not be there the next time setContent was called?

Thanks, and apologies in advance if I'm missing obvious stuff

More Details About Repo
Owner Name facebook
Repo Name react
Full Name facebook/react
Language JavaScript
Created Date 2013-05-24
Updated Date 2022-09-27
Star Count 195296
Watcher Count 6655
Fork Count 40431
Issue Count 1113

YOU MAY BE INTERESTED

Issue Title Created Date Comment Count Updated Date
Add a test for a raw key agreement algorithm used for psa_key_derivation_setup 0 2022-05-19 2022-08-10
What to do about whole-script homographs [M1332714] 1 2017-04-21 2022-08-07
Failed prop type: The prop `option.name` is marked as required in `Option`, but its value is `undefined`. 1 2021-04-02 2022-07-17
A question about the view point when calculating eigenvectors 1 2022-07-01 2022-07-24
Can't use with .dev domain 3 2022-06-02 2022-09-15
Anvil region file support? 1 2022-05-07 2022-09-09
How use navigate function on JS 3 2022-05-09 2022-09-18
Upgrading to Px4 Firmware 1.12 with Coex Pix 7 2022-02-01 2022-07-28
PREAPPROVE approves token every time you start the bot 1 2021-09-20 2022-09-19
Problem with missing data 1 2022-08-26 2022-09-25
从mysq 5.7.27 往sqlserver 2012同步数据启动报错,帮忙看下 6 2020-01-19 2022-09-16
The private thread was created to only owner user 1 2019-07-03 2022-09-25
How to disable kindle-dash? 3 2021-02-09 2022-09-14
Allow usage of HW backed devices as management port in the full mode 0 2022-09-12 2022-09-19
How playground native.wasm is built ? 2 2021-12-08 2022-08-20
Building v5.1.6 gives error: 'zlib_filefunc_def' {aka 'struct zlib_filefunc_def_s'} has no member named 'zopendisk_file' 26 2022-01-10 2022-08-30
[Bug]: golang的agent在grpc中使用了一元拦截器(UnaryInterceptor)的时候报错 1 2022-07-14 2022-08-14
WebGL is enabled by default, README and --help says it's off by default 0 2021-06-01 2022-08-29
Large bracketed pastes are very slow (seems `O(n^2)`) 14 2022-03-09 2022-09-06
Tried updating WSA but it never completes and no apps can be open now 0 2022-08-07 2022-08-10
Redirects for nightly builds 4 2022-03-13 2022-09-18
Equation equality-semigroup-mult not numbered in subsection 2.14.2 1 2021-11-05 2022-09-18
Broken links on https://homotopytypetheory.org/book/ 2 2022-03-18 2022-09-18
Fails to build on 32-bit architectures 1 2021-02-28 2022-09-16
Additional Error checking on update 0 2021-10-29 2022-09-13
Migrate to null safety 0 2021-03-03 2022-09-15
Driver request for Sensirion SFA3x sensor ( I2C ) 15 2021-07-09 2022-09-25
undefined is not a function Error is on audioTracks.enabled = true 1 2021-10-22 2022-09-25
Dog biscuit recipe doesn't make dog biscuits! 2 2022-04-19 2022-09-03
"Name Error" in code cell 3 2021-11-18 2022-09-17
M1 Mac create instance failed 2 2021-02-08 2022-07-16
passing an empty column array to Doctrine\DBAL\Schema\Table::setPrimaryKey() creates an empty primary key 0 2021-10-21 2022-09-05
Sekai Komik: Changed their domain 2 2022-04-23 2022-09-03
Support other architectures also supported by conda-forge (e.g. Mac arm64) 10 2021-04-07 2022-09-01
Step "Creating a transformation with MLLR", log out so many "INFO: mllr.c(186): Estimation of 0 th regression in MLLR failed". 0 2022-08-26 2022-09-15
error when executing the autogen in ubuntu 2 2022-09-03 2022-09-15
Autocomplete not working for sass `@use` statements 0 2022-05-30 2022-09-17
How to use the result of task after it's completed? 2 2022-04-04 2022-09-26
Tracking integration for Video to Text (video-text retrieval) 0 2021-11-18 2022-09-25
Redis RRD timeseries corrupted? Cannot write data - timeseries.lua / rrd.lua fails 2 2022-08-25 2022-09-08
Maximum password length: "Provided password longer than supported in command line utility" 1 2021-12-30 2022-08-27
Update framework function to get active configuration when component is `analysis` 1 2022-06-21 2022-08-23
Manual Windows Wazuh agent upgrade hangs/fails 13 2022-06-21 2022-09-03
Incorrect Kernel Information 2 2022-04-12 2022-09-20
Documentation: Recommendation of using prettier-eslint 4 2018-11-14 2022-09-17
Create a Podman performance tips tutorial 3 2022-07-12 2022-09-25
Get method signature location (line/column numbers) through mono.cecil 0 2022-05-18 2022-09-06
Duplicate ID in Component 1 2022-06-13 2022-09-15
"SELECT COUNT(*) FROM sqlite_schema;" always returns null 13 2022-06-27 2022-09-25
Update engine Perfetto/trace_to_text dependency to a build that supports Apple Silicon 1 2022-07-24 2022-09-13