InfiniteLoop in useEffect when one of the dependency is a function from useContext

Viewed 423

2021 UPDATE

Use a library that makes requests and cache them - react-query, swr, redux-toolkit-query

ORIGINAL QUESTION

I've been struggling with this for quite a long time and didn't find an answer.

I have a component that is the last step of some registration process during which I ask a user to enter its data through several forms. In this component, I send collected data to API. If the request is successful I show ok, if not I show error.

I have useEffect that sends the data. The function that performs this task lives in a context

  const { sendDataToServer } = useContext(context)

  useEffect(() => {
    const sendData = async () => {
      setLoading(true)
      await sendDataToServer(...data)
      setLoading(false)
    }
    sendData()
  }, [sendDataToServer, data])

If I include sendDataToServer in the dependencies list this useEffect would go into an infinite loop, causing endless rerendering. I suppose this is because a reference to the function has a different value on every render.

I can of course redesign the app and do not keep the function in the context, but I do like it and don't consider it a bad practice (correct me if I am wrong)

So what are my options here? How do I keep the flow with the context API, but use useEffect with the correct list of dependencies?

1 Answers

You're right with your guess, that's why we got useCallback for referential equality.

You didn't post the sendDataToServer function, but it should look something like this with useCallback:

const sendDataToServer = useCallback(data => {
    // your implementation
}, [your, dependencies])

After that you can safely use it in your useEffect.

I highly recommend Kent C. Dodd's blog posts: When to useMemo and useCallback

Smartassing now: If it's only purpose is sending data to the server (and not changing the app's state), I don't know why it should be part of the context. It could be a custom hook or even a static function.

Btw: There could be another problem: If the data dependency in your useEffect is changed when executing sendDataToServer, you will still have an endless loop (e. g. when you fetch the new data after executing sendDataToServer), but we can't see the rest of the code.

Related