Search code examples
javascriptreactjsreact-hooksuse-effect

Unnecessary parameter in useEffect dependency array


I'm creating an application where users can create and share notes. To share each other's notes users have to send requests to specific users. The requests are fetched whenever home is loaded. However, requests is a context since it is also consumed in the toolbar and requests page to show the presence of the requests When I'm using setRequsts method of the context to set all the requests after home loads, the fetch goes into an infinite loop of /noteand /me URLs, since the setRequests method is also provided in the dependency array of useEffect When removed, useEffect show missing dependencies. What's the work around?

  const {setRequests } = useContext(RequestsContext)
  const [notes, setNotes]    = useState([])
  const [fetched, setFetched] = useState('')
  const { isAuthenticated } = props
  const {page}=useContext(PageContext)
  const [sortBy,setSortBy]=useState('latest')

  useEffect(() => {
    const fetch = async () => {
      try {
        let url = 'http://192.168.56.1:5000/api/v1/note', p, sort
        if (page) p = `?page=${page}&limit=12`
        if (sortBy === 'latest') {
          sort=''
        } else if (sortBy === 'most_liked') {
          sort='&sort=likes'
        }
        const res = await Axios.get(url+p+sort)
        setNotes(res.data.data)
        if (res.data.data.length > 0) {
          setFetched('Y')
        } else {
          setFetched('N')
        }
      } catch (err) {
        console.log(err)
      } finally {
        if (isAuthenticated) {
          const fetch = async () => {
            const res = await axios.get(`user/me`)
            if (res.data.data.createdPosts.length > 0) {
              const arr = res.data.data.createdPosts.map(el => el.request)
              console.log(arr)
              setRequests(arr)
            }
          }
          fetch()
        }
      }
    }
    fetch()
  }, [isAuthenticated, /* setRequests, */ page, sortBy])

Solution

  • The problem is that the context provides a technically different setRequests function on each render (that have a different address). This causes useEffect to fire on each render.

    To work around this, you could wrap setRequests in a useCallback() hook, like so:

    // ...
    const wrappedSetRequests = useCallback(setRequests, []);
    // ...
    
    useEffect(() => {
      // do your stuff using 'wrappedSetRequests' instead of setRequests.
    }, [ wrappedSetRequests /*...*/ ]);