Search code examples
javascriptreactjsrxjsrxjs-observables

Why is my RXJS epic in an infinite loop on mount but only called once on button click?


please take a look at this code below

basically what is happening my action is being dispatched here:

   useEffect(() => {
        fetchData()
        setLoaded(true)
    }, [])

but for some reason this is infinite looping and causing my action to be dispatched continuously

export const fetchData = () => ({ type: 'GET_USER_DATA' })

and this is triggering my epic

const getUserData = (action$, state$) =>
    action$.pipe(
        ofType('GET_USER_DATA'),
        mergeMap(
            (action) =>
                ajax
                    .getJSON(
                        `myurlishere`,
                    )
                    .pipe(map((response) => fetchUserFulfilled(response))),
        )
    )

which trigger this:

const fetchUserFulfilled = (payload) => ({ type: 'GET_DATA_SUCCESS', data: payload })

this code all works but it's continuously calling it in an infinite loop

however, if I move the code from useEffect to a button call like so:

 <button onClick={fetchData}>fetch</button>

it only calls it once, which is what I want

but I need the data to be called onmount. so how do I fix it?

please note I have tried adding various things to the second argument of useEffect but it's having no effect

   useEffect(() => {
        fetchData()
        setLoaded(true)
    }, [user.id])

Solution

  • Based solely off the provided code, I don't see any issues. My gut suggest a few options, even though you mentioned some of them I would triple check:

    1. Missing dependencies array as second arg to useEffect, or you're using a variable for it but the variable has an undefined value which would have the same problem.
    2. If you are using useEffect dependencies, perhaps one of them is constantly changing unknowingly. e.g. objects often change in identity between renders, {} !== {}
    3. There is code not shown that is also dispatching the same action, and in fact that useEffect is only running once.
    4. Some parent is rendering one of the ancestors or this component with a key={something} and the value provided changes on each render. If that happens, the component is torn down and recreated every time from scratch.

    If you are 100% positive you are providing useEffect(work, []), an empty array as second argument, but the effect is in fact confirmed to be running in an infinite loop, synchronously, then the forth possibility is likely.

    If you typed these code examples in the question by hand when posting this, do not trust that you implemented them the same way as what you think your app is doing. Triple check. Ideally have someone else check who didn't write the code. Often the problem is what we think we've told our code to do is not what we've actually told it to do. If you haven't already, your best bet is to step through the code with a debugger so you can see what's happening.

    Hope this helps!