I have a use-case where a page have to call the same fetch function on first render and on button click.
The code is similar to the below (ref: https://stackblitz.com/edit/stackoverflow-question-bink-62951987?file=index.tsx):
import React, { FunctionComponent, useCallback, useEffect, useState } from 'react'; import { fetchBackend } from './fetchBackend'; const App: FunctionComponent = () => { const [selected, setSelected] = useState<string>('a'); const [loading, setLoading] = useState<boolean>(false); const [error, setError] = useState<boolean>(false); const [data, setData] = useState<string | undefined>(undefined); const query = useCallback(async () => { setLoading(true) try { const res = await fetchBackend(selected); setData(res); setError(false); } catch (e) { setError(true); } finally { setLoading(false); } }, []) useEffect(() => { query(); }, [query]) return ( <div> <select onChange={e => setSelected(e.target.value)} value={selected}> <option value="a">a</option> <option value="b">b</option> </select> <div> <button onClick={query}>Query</button> </div> <br /> {loading ? <div>Loading</div> : <div>{data}</div>} {error && <div>Error</div>} </div> ) } export default App;
The problem for me is the fetch function always triggers on any input changed because eslint-plugin-react-hooks
forces me to declare all dependencies (ex: selected state) in the useCallback
hook. And I have to use useCallback
in order to use it with useEffect
.
I am aware that I can put the function outside of the component and passes all the arguments (props, setLoading, setError, ..etc.) in order for this to work but I wonder whether it is possible to archive the same effect while keeping the fetch function inside the component and comply to eslint-plugin-react-hooks
?
[UPDATED] For anyone who is interested in viewing the working example. Here is the updated code derived from the accepted answer. https://stackblitz.com/edit/stackoverflow-question-bink-62951987-vxqtwm?file=index.tsx
Advertisement
Answer
Add all of your dependecies to useCallback
as usual, but don’t make another function in useEffect:
useEffect(query, [])
For async callbacks (like query in your case), you’ll need to use the old-styled promise way with .then
, .catch
and .finally
callbacks in order to have a void function passed to useCallback
, which is required by useEffect
.
Another approach can be found on React’s docs, but it’s not recommended according to the docs.
After all, inline functions passed to useEffect
are re-declared on each re-render anyways. With the first approach, you’ll be passing new function only when the deps of query change. The warnings should go away, too. 😉