0

I can't seem to find an answer to this despite trying different things. I keep getting the error:

 Line 18:5:  React Hook useEffect has a missing dependency: 'execute'. Either include it or remove the dependency array  react-hooks/exhaustive-deps
printWarnings

on this file:

import {useEffect, useState} from 'react'

export const createEffect = (fs, dependency) => {
    const[data, setData] = useState({})

    const execute = () => (
        fs().then(res => {
            setData(res)
        }).catch(err => {
            // do some magic
        })
    )

    useEffect(() => {
        execute()
        const interval = setInterval(execute, 15000)
        return(() => clearInterval(interval))
    }, [dependency])

    return(data)
}

The TLDR is I need to make a fetch request to a specific API function defined by fs() either every 15 seconds or when some global state changes, defined by dependency. I also want to capture the data and any errors hence why I'm wrapping the fs() around with a couple of then() and catch() blocks.

The following code gives me the warning at the top when I execute it inside my functional component:

import createEffect from './components/createEffect'
~~~
let api = new api('http://localhost:8080/v1');
const[status, updateStatus] = useState(true);
const summary = createEffect(api.getSummary, status)

Any idea how I can fix this? I'm new to React useEffect and not 100% if this is the right way to go about doing something like this? Specifying execute as a dependency inside my useEffect call causes the app to constantly re-render. I'm running execute() before calling setInterval() because I want the query to run every 15 seconds AND when the component first renders.

For more context: I'm making a dashboard as a sample project and want to query multiple api endpoints every 15 seconds or immediately when some global state changes. If the global state does change, I want to reset my HTTP polling from whatever time that is left back to 15 seconds.

Harsh Baid
  • 59
  • 8
  • 2
    Are you using `execute` anywhere other than inside the useEffect? If not, you could simply move the declaration inside of the useEffect to get rid of the warning – Jon Warren Nov 11 '19 at 22:53
  • if I move `execute()` inside my useEffect function, I get the same error expect this time it's about the `fs()` function: `React Hook useEffect has a missing dependency: 'fs'` – Harsh Baid Nov 11 '19 at 22:57

2 Answers2

3

The linting error is very straight just says exactly what you need to do, either leaving the dependency array of useEffect as empty, or add the execute function into it.

Basically, because you access the execute function inside the effect callback, and as it's created within the same scope as the effect, React requires it to be a dependency.

To resolve the problem with continuous re-rerendering, you need to make your execute function a memoized function by using useCallback

Solving it should be as simple as:

const execute = useCallback(() => (
    fs().then(res => {
        setData(res)
    }).catch(err => {
        // do some magic
    })
), [setData]);

useEffect(() => {
        execute()
        const interval = setInterval(execute, 15000)
        return(() => clearInterval(interval))
    }, [execute])
Thai Duong Tran
  • 2,453
  • 12
  • 15
  • I did it exactly your way, but now the linter is complaining about the `fs()` function `React Hook useCallback has a missing dependency: 'fs'` Adding fs as a dependency causes it to constantly re-render. – Harsh Baid Nov 11 '19 at 23:01
  • Where your `fs` function comes from ? Can you update your question to include it ? – Thai Duong Tran Nov 11 '19 at 23:10
  • I'm not sure what additional information I can provide? The `fs` function is a function that is passed in when I import the file and call `createEffect()` since createEffect takes two parameters, a function to execute `fs` and some `dependency` to be added to the useEffect call inside createEffect. The createEffect function is exported like this `export const createEffect = (fs, dependency) => {}` and is called like this: `createEffect(() => api.getSummary(), status)` – Harsh Baid Nov 11 '19 at 23:15
  • If you're asking what the fs actually is. It's simply a `fetch()` call to an API which returns a promise. – Harsh Baid Nov 11 '19 at 23:16
  • If you call `createEffect` like this `createEffect(() => api.getSummary(), status)` then surely you will end up having the re-rendering problem because a new function `() => api.getSummary()` is created inside the component scope every single time the component is considered for re-rendering. In JS, function is an object React shallow comparison will consider them different. To solve the problem you have some choice, either taking it out of the component, or make it a memoized function with `useCallback`. – Thai Duong Tran Nov 11 '19 at 23:47
  • Darn, I was hoping I could isolate my useEffect call into a separate component and then just pass in the api function and return a value. That way I wouldn't have to implement any logic within my main component. Not sure if this is valid, but could I jut surpress the linting error? Would it have any effect on my component and the way it functions. – Harsh Baid Nov 12 '19 at 01:37
0

I think you can add execute as a dependency, since the function definition itself isn't changing. Just debug if the component is re-rendering after every call. I think they shouldn't.

Y M
  • 2,105
  • 1
  • 22
  • 44