0

I am trying to dispatch a redux action in the componentDidUpdate lifecycle method, however it results in an infinite loop that causes the application to crash. Here's the code I currently have:

   componentDidUpdate(prevProps, prevState) {
    const { dispatch, sockOpen, user } = this.props;
    if (
      navigator.onLine &&
      user &&
      (sockOpen !== prevProps.sockOpen || sockOpen)
    ) {
      dispatch(pollStartAction());
      this._checkSessionIntervalId = setInterval(
        this.checkActiveSessions,
        5000
      );
    }
  }

Is this the right way of doing this or is there a better of writing this that wouldn't result in an infinite render loop. Thanks.

Dave Kalu
  • 1,520
  • 3
  • 19
  • 38
  • 1
    What is your action doing in the reducer. If you state is updating each time the action is called, you will get this infinite loop. Depending on your reducer and what your action creator is doing, you might need some middleware to debounce the requests. Need more information to help. – mattdevio Sep 10 '18 at 19:24
  • My action is not in the reducer. It is an actions.js file and I'm just dispatching the action from the component. However, what I am trying to achieve is to dispatch an action when my component gets updated. – Dave Kalu Sep 10 '18 at 19:35

2 Answers2

3

Presumably dispatching that action is updating the store, which is updating props, which triggers componentDidUpdate again. The usual way around this is to have some conditional that eventually stops the action from dispatching again, which it looks like you're trying to do, but that conditional is always evaluating to true. Without knowing what the parts of that conditional mean for sure, I'd guess that the problem is something with:

(sockOpen !== prevProps.sockOpen || sockOpen)

whenever sockOpen is truthy, the dispatch will run and you'll loop. Whenever sockOpen's value is different from the previous update, it will also loop. So, even if sockOpen is falsey, it will still loop if sockOpen was truthy last time. That means if sockOpen is ever truthy, the loop becomes infinite, since even if something happens to make sockOpen falsey, it was truthy on the last loop, and the first part of that or clause is true.

You need to figure out what the conditions are where that dispatch should NOT happen, set the terms of conditional to match those, and most importantly make sure it's possible for that set of conditions to occur!

Steve Archer
  • 641
  • 4
  • 10
  • Ahh, I see. I might have to move the `pollStartAction` to the `componentDidMount` since I only need to run it when the component mounts but I will have to leave the `checkValidSessions` block in the `componentDidUpdate` because that's the only place I have access to the updated `sockOpen` flag which is expected to be `truthy`. – Dave Kalu Sep 10 '18 at 20:43
1

Your logic is wrong. In (sockOpen !== prevProps.sockOpen || sockOpen), once sockOpen is true, it will always run the pollStartAction() logic. If you want that to run once when it turns true, then you need it to be && sockOpen instead.

markerikson
  • 63,178
  • 10
  • 141
  • 157
  • I am not trying to run the pollStartAction() logic once, I actually want to run it every time the component gets remounted and this depends on the sockOpen variable to be true. That's why I wrote the `sockOpen !== prevProps.sockOpen || sockOpen`. Initially `prevProps.sockOpen` is false and sockOpen is true which explains why the first half of the comparison whereas the second part is meant to match when the sockOpen is true nonetheless. I might be mixing it up but that's basically the idea of what I am trying to accomplish. Thanks. – Dave Kalu Sep 10 '18 at 19:47
  • if you want to run it when then component gets mounted, you should dispatch the action in componentDidMount – Steve Archer Sep 10 '18 at 19:49
  • I can't dispatch the action in `componentDidMount` because `sockOpen` is false in `componentDidMount` and there is no way to know when it changes from the `componentDidMount`. And from the code, `sockOpen` must be true to call `pollStartAction()`. – Dave Kalu Sep 10 '18 at 19:58
  • 1
    But, that logic will _also_ execute _every time the component re-renders_, which means that once `sockOpen` is true, you'll keep stepping into that dispatch sequence many times. That's _not_ what you want. Also, "every time the component gets remounted" is wrong. When a component is unmounted, that instance is destroyed. It seems like you'd need to keep track of an additional instance field and use that in the condition, like: ` && sockOpen && !this._checkSessionIntervalId)`. – markerikson Sep 10 '18 at 20:13
  • That makes sense. I just tried placing the `pollStartAction()` in the `componentDidMount` and only left the `checkActiveSessions` code block in the `componentDidUpdate` then changed the condition to `sockOpen !== prevProps.sockOpen`. This works fine so far. I believe there's a better way of doing this. Thanks for the correction :) – Dave Kalu Sep 10 '18 at 20:40
  • Hi @markerikson, thanks for helping out this. Please can you take a look at this https://stackoverflow.com/questions/52269348/initialize-socket-connection-only-when-a-condition-is-met. I'll appreciate if you could help. Thanks. – Dave Kalu Sep 11 '18 at 11:11