Skip to content

[Proposal] Keep RCTPropsAnimatedNode and connection to UIView around until _delete_ animation completes. #17475

@gavrix

Description

@gavrix

This is an issue of 2 independent animation modules in RN not playing nicely together.

  1. Animated — animations are being removed as soon as the associated React component is removed for the graph.
  2. LayoutAnimation — since it wraps UIView- related manipulation inside some animation blocks — those animations are still running when React view is gone but corresponding UIView is still being removed with animation. (UIView will be completely removed when delete animation completes).

Now, what is the issue: if both of those animation mechanisms are used it is possible to have a situation when UIView is still on screen, but Animated's animation run on that view would be prematurely terminated.

Is this a bug report?

Yes

Have you read the Contributing Guidelines?

Yes

Environment

Environment:
OS: macOS High Sierra 10.13.2
Node: 8.9.2
Yarn: Not Found
npm: 5.5.1
Watchman: 4.9.0
Xcode: Xcode 9.2 Build version 9C40b
Android Studio: 3.0 AI-171.4443003

Packages: (wanted => installed)
react: 16.2.0 => 16.2.0
react-native: 0.49.5 => 0.49.5

Steps to Reproduce

Consider app provided in demo section (on Expo)

App represents a list where each item is a swipe-to-reveal. There is context "Remove" action under each item.
Revealing is done via Animated module and achieved using translateXtransform.
These are the steps to reproduce visual glitch:

  1. Swipe left to reveal some items "Remove" context action
  2. Press Remove
  3. Observe transitions and animations within next second.

Expected Behavior

According LayoutAnimation, item being removed is supposed to fade away, item right after it is supposed slide in on removed item's position. Visual state of item being removed is not changed except opacity animation.

Actual Behavior

Visual state of the item being removed immediately resets to default, wiping out translateX transform. This happens because Animation Node used to control that transform value is disconnected from React component that was just removed from the graph. On the native side, RCTPropsAnimatedNode upon disconnection calls restoreDefaultValues that removes any transform from the native UIView.

Reproducible Demo

Here's the link to Expo fragment demonstrating described behaviour: https://snack.expo.io/HyiDbGeEG

Thoughts

There is quick ways of workaround that glitch (that quickly comes to mind): subclass Animated.View and override componentWillUnmount. That will disable disconnecting AnimatedNode from View and will prevent restoreDefaultValues.

However, I think there is something wrong with the basics. We connect AnimatedNode to the view via reactTag. On each update that Node attempts to receive UIView from RCTUIManager to update UIView's state.
I think it is wrong to disconnect Node from the view when corresponding React component goes away — because corresponding UIView (to which AnimatedNode is in fact connected to via reactTag) may still be on screen.

So here's my proposal: let RCTUIManager notify RCTNativeAnimatedNodesManager that view with that reactTag is going away (either as layout update OR as deleteAnimations for that view completed). Then RCTNativeAnimatedNodesManager can locate node connected to that viewTag and disconnect it.

This scheme will allow to perform custom custom deleting animations beyond what's supported in LayoutAnimation.

In demo app provided — what I actually wanted to do from the beginning was to perform animated "unswipe" as item was being removed from the screen.

I have a quick PoC utilizing proposed changes to RCTUIManager and RCTNativeAnimatedNodesManager showcasing this behaviour in demo app:

swipe-to-delete-recording mov

I would like to receive public opinion on proposed changes before wrapping my changes to RCTUIManager and creating PR.

Is that something you would consider? Is there something I am missing with regards to how native machinery works behind RN on iOS that my proposed changes would break?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Ran CommandsOne of our bots successfully processed a command.Resolution: LockedThis issue was locked by the bot.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions