react-native: regression on InteractionManager.runAfterInteractions(f), not always calling f

Since RN 0.28, InteractionManager.runAfterInteractions(f) happen to sometimes never call f. I couldn’t reproduce it with some simple code, but I have an app that schedule stuff in background when leaving a screen, and use InteractionManager.runAfterInteractions when re-entering the screen (which happen during the navigator transition) and sometimes f is not called.

Should we assume that runAfterInteractions can sometimes not happen, since it’s a cancellable now? (but i’m not using cancel() anywhere in my code) or is this a bug?

Thanks

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 29
  • Comments: 47 (28 by maintainers)

Commits related to this issue

Most upvoted comments

quick workaround if anyone interested.

import { InteractionManager } from "react-native";
export default {
  ...InteractionManager,
  runAfterInteractions:  f => {
    // ensure f get called, timeout at 500ms
    // @gre workaround https://github.com/facebook/react-native/issues/8624
    let called = false;
    const timeout = setTimeout(() => { called = true; f() }, 500);
    InteractionManager.runAfterInteractions(() => {
      if (called) return;
      clearTimeout(timeout);
      f();
    });
  }
};

@Exilz @jqn @aforty I had this exact issue. Solved it: The callback for InteractionManager.runAfterInteractions was never being called. Turns out that when components are unmounted, their animations are not stopped completely. I had a Animated.decay animation which seems to make InteractionManager never return. Calling this.myDecayAnimation.stopAnimation() in componentWillUnmount() did not work for me (more on this below).

For me, a couple of things worked:

  1. Use isInteraction: false. E.g. Animated.decay(this.myDecayAnimation, {velocity: 1/5, isInteraction: false}); This is an undocumented feature that tells RN that your animation will not block InteractionManager. This solved it for me, but I kept looking.
  2. Removing the callback for Animated.Value().start(...) where I was conditionally repeating the animation.

Regarding 2: The callback was checking this.state.shouldAnimate === true. If true, it re-ran the animation. The problem was that the callback was being run after the component unmounted, but without access to the correct state, resulting in the animation being run a second time, thus making my stopAnimation useless.

You may have similar looping animations or stopAnimation code that isn’t really stopping your animation, or you could be assuming that your animation stops when the component is unmounted, regardless of your loops. Re-check all those assumptions and see where it gets you.

In my case it was a looping animation (i.e. with the Animated API) that prevented f from being called. For now I’m going to replace the animation with a static element. I wonder if it’s possible to make runAfterInteractions work with an infinitely looping animation elsewhere.

this is still happening on RN51

Has anyone figured out a fix for this issue. I randomly started experiencing this with RN 0.40 Android. It used to work great before.

This is a major issue for me as well and not consistently reproducible either. I hope someone tracks this down. Every time I think I think it’s fixed it rears its ugly head again and causes my components not to load after navigation.

Ok, so seems InteractionManager not calling the callback also happens for me. I added a breakpoint and found that _interactionSet.size is always >= 1, so something in not clearing the interaction handle. I don’t use createInteractionHandle in my code, and I don’t think any packages I use, use it either.

Edit: Seems if the panHandler gets removed before releasing the touch, it never clears the handle

@gre: no, I’m not sure where you got that. All tasks scheduled with runAfterInterActions should be executed in order after interactions complete. If the interactions never complete (e.g. infinite animation) then those tasks (and any subsequently scheduled tasks) will ever get run. If you ever schedule tasks A then B with runInteractionManager and you don’t see A run before B but B does run, there is definitely a bug (assuming you didn’t cancel A).

Separately, animations should be stopped when the component being animated unmounts, which should allow runAfterInteractions to continue if those animations were blocking it. If that’s not happening, that’s also a bug, although the previous reports indicate that the problems were in user code erroneously creating an infinite loop.

I think the crux here is if the task is forever delayed (e.g. by animations running forever), of if the task is getting dropped entirely.

When you see the call not happen when you expect, do any further runAfterInteractions tasks get executed? If the problem is an infinite animation, I would expect the whole runAfterInteractions queue to get wedged. If hats not happening, there is definitely a bug.

And as @satya164 said, if you don’t want the task to wait on interactions or animations, don’t use runAfterInteractions 😛. If you want to do things while running an animation, you will likely drop animation frames - that’s the whole point - but the better fix is to switch to useNativeDriver which shouldn’t have that problem since it runs natively and will not be interrupted by any slow JS execution.

Getting bit badly by this.