facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
118.51k stars 24.27k forks source link

Animated Value listeners removed after bound elements unmounted #43586

Open elliottkember opened 6 months ago

elliottkember commented 6 months ago

Description

If an animated value is attached to an animated view as a transform, and the view is unmounted, all listeners on the animated value are removed and cease to fire. The animation continues to be active, and new listeners can be added, but the original listeners are all removed. This does not happen in react-native-web, and expo snack cannot replicate the problem.

Steps to reproduce

The animated value listener attached to the animated value has stopped listening now, and won't fire any more.

Findings

In Libraries/Animated/useAnimatedProps.js#167:

  useLayoutEffect(() => {
    node.__attach();
    if (prevNodeRef.current != null) {
      const prevNode = prevNodeRef.current;
      // TODO: Stop restoring default values (unless `reset` is called).
      prevNode.__restoreDefaultValues();
      prevNode.__detach();
      prevNodeRef.current = null;
    }
    return () => {
      if (isUnmountingRef.current) {
        // NOTE: Do not restore default values on unmount, see D18197735.
        node.__detach();   <-------- here
      } else {
        prevNodeRef.current = node;
      }
    };
  }, [node]);

This line seems to be detaching the animated props node, which calls removeAllListeners on the AnimatedNode and kills the listener. However, an animated value can have a lifespan longer than any component that uses it and may have other listeners that are doing important things, so I think this is a bug – especially because it doesn't happen in react-native-web.

I suspect the fix may involve tracking which listeners are added specifically by the node and removing those specifically (instead of calling removeAllListeners). I don't know what level of effort is involved here but I would be more than happy to help test or prototype a fix for this.

React Native Version

0.73.6

Affected Platforms

Runtime - Android, Runtime - iOS

Output of npx react-native info

info Fetching system and libraries information...
System:
  OS: macOS 14.3.1
  CPU: (10) arm64 Apple M1 Max
  Memory: 142.38 MB / 64.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 20.8.1
    path: ~/.nvm/versions/node/v20.8.1/bin/node
  Yarn:
    version: 1.22.21
    path: /opt/homebrew/bin/yarn
  npm:
    version: 10.1.0
    path: ~/.nvm/versions/node/v20.8.1/bin/npm
  Watchman:
    version: 2024.01.22.00
    path: /opt/homebrew/bin/watchman
Managers:
  CocoaPods:
    version: 1.15.2
    path: /Users/elliott/.rbenv/shims/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 23.4
      - iOS 17.4
      - macOS 14.4
      - tvOS 17.4
      - visionOS 1.1
      - watchOS 10.4
  Android SDK:
    API Levels:
      - "28"
      - "33"
      - "34"
    Build Tools:
      - 28.0.3
      - 29.0.2
      - 29.0.3
      - 30.0.2
      - 30.0.3
      - 31.0.0
      - 33.0.0
      - 34.0.0
    System Images:
      - android-33 | Google APIs ARM 64 v8a
      - android-34 | ARM 64 v8a
      - android-34 | Google APIs ARM 64 v8a
      - android-34 | Google Play ARM 64 v8a
    Android NDK: Not Found
IDEs:
  Android Studio: 2022.3 AI-223.8836.35.2231.10406996
  Xcode:
    version: 15.3/15E204a
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.10
    path: /usr/bin/javac
  Ruby:
    version: 3.0.0
    path: /Users/elliott/.rbenv/shims/ruby
npmPackages:
  "@react-native-community/cli": Not Found
  react:
    installed: 18.2.0
    wanted: 18.2.0
  react-native:
    installed: 0.73.6
    wanted: 0.73.6
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: false
iOS:
  hermesEnabled: true
  newArchEnabled: false

Reproducer

https://github.com/elliottkember/animation-listener-bug

Screenshots and Videos

In this video, the Remount animation button simply changes the key on the Animated.View element that keeps changing size.

https://github.com/facebook/react-native/assets/42827/eab17e92-edd1-404a-963a-0a003d14c1ef

tjom commented 4 months ago

Hi @elliottkember, thank you for the repro! It seems that this is the same issue we are experiencing after upgrading from react native 0.71.x.

To verify that the issue was introduced in 0.72, I created a fresh react native project with v0.71.19 and added your App.js code here. The animation value is still updated correctly after pressing the remount button.

I then upgraded the repo to react native 0.72.0 (see here) and ran the test again, which resulted in the same issue you are describing above.