0

Running into an infinite loop when I try to dispatch an action which grabs all recent posts from state.

I have tried the following in useEffect dependency array

  • Object.values(statePosts)
  • useDeepCompare(statePosts)
  • passing dispatch
  • omitting dispatch
  • omitting statePosts
  • passing statePosts
  • doing the same thing in useCallback

a lot of the suggestions came from here

I have verified that data correctly updates in my redux store.

I have no idea why this is still happening

my component

  const dispatch = useDispatch()
  const { user } = useSelector((state) => state.user)
  const { logs: statePosts } = useSelector((state) => state.actionPosts)

  const useDeepCompare = (value) => {
    const ref = useRef()
    if (!_.isEqual(ref.current, value)) {
      ref.current = value
    }
    return ref.current
  }

  useEffect(() => {
    dispatch(getActionLogsRest(user.email))
  }, [user, dispatch, useDeepCompare(stateLogs)])

actionPosts createSlice

const slice = createSlice({
  name: 'actionPosts',

  initialState: {
    posts: [],
  },
  reducers: {
    postsLoading: (state, { payload }) => {
      if (state.loading === 'idle') {
        state.loading = 'pending'
      }
    },
    postsReceived: (state, { payload }) => {
      state.posts = payload
    },
  },
})

export default slice.reducer

const { postsReceived, postsLoading } = slice.actions

export const getActionPostsRest = (email) => async (dispatch) => {
  try {
    dispatch(postsLoading())
    const { data } = await getUserActionPostsByUser({ email })
    dispatch(postsReceived(data.userActionPostsByUser))
    return data.userActionPostsByUser
  } catch (error) {
    throw new Error(error.message)
  }
}
asus
  • 1,427
  • 3
  • 25
  • 59

1 Answers1

0

Remove dispatch from dependencies.

 useEffect(() => {
    dispatch(getActionLogsRest(user.email))
  }, [user, dispatch, useDeepCompare(stateLogs)])

you cannot use hook as dependency and by the way, ref.current, is always undefined here

const useDeepCompare = (value) => {
    const ref = useRef()
    if (!_.isEqual(ref.current, value)) {
      ref.current = value
    }
    return ref.current
  }

because useDeepCompare essentially is just a function that you initiate (together with ref) on each call, all it does is just returns value. That's where the loop starts.

Aleks
  • 894
  • 10
  • 14
  • removing dispatch from dep array does not fix the issue – asus Sep 29 '20 at 16:22
  • yeah, you need to remove circular dependency. What does `dispatch(getActionLogsRest(user.email))`? Updates logs? Then do a proper compare of `stateLogs`. I would suggest useMemo for that. – Aleks Sep 29 '20 at 16:26
  • fetches logs from api. would u recommend doing the compare in the action? reducer? useEffect? – asus Sep 29 '20 at 16:28
  • Without knowing much about your code, I would suggest trying `useMemo` hook first. Something like this `const boolean = useMemo(()=>{deepCompare(stateLogs)},[stateLogs])` Put ref outside `deepCompare()` function. Or lift it up to global state. – Aleks Sep 29 '20 at 16:35
  • Then pass result to `useEffect` dependencies, `[user, boolean]` – Aleks Sep 29 '20 at 16:37
  • `boolean` could be any value really, but whenever it changes useEffect will trigger – Aleks Sep 29 '20 at 16:41
  • it says I can't call hooks inside `useMemo` . unless `deepCompare` in your above comment is a function that is not part of my code – asus Sep 29 '20 at 16:52
  • yes, just rename useDeepCompare to deepCompare and use it as regular function, move ref `const ref = useRef()` outside that function. – Aleks Sep 29 '20 at 16:53
  • The only problem with this solution is that on every change in `stateLogs` `useMemo` will run. A proper alternative would require you listen and compare to object fields, not the whole object. Like timestamp for example. – Aleks Sep 29 '20 at 16:53
  • Could u elaborate on the timestamp solution? – asus Sep 29 '20 at 16:55
  • 1
    If you logs have timestamps, you can access it and pass the latest one to useEffect as milliseconds. If it is a new one, render will happen. You obviously will have to parse through logs, collect timestamps and then sort them but that will be faster than comparing 2 large objects anyway. – Aleks Sep 29 '20 at 17:02