question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Memory leak when updating stateful value in component

See original GitHub issue

Description

After implementing a swipe gesture on list items inspired by SwipeableListExample.js I noticed a memory leak in my app. I’ve been able to reproduce with minimal changes to SwipeableListExample.js in Reanimated example project.

It seems that calling a function declared in the same component as the worklet calling it, is creating a memory leak.

Maybe I’m misusing the library but I haven’t found any reference to that in the documentation so I’m assuming this is a bug.

Steps To Reproduce

  1. Open SwipeableListExample.js in the Example project
  2. Declare a stateful value with useState() in the ListItem component.
  3. Change the stateful value in the useAnimatedGestureHandler worklet.
  4. Take an initial memory snapshot using Safari
  5. Run project and navigate multiple times between home and the Swipeable List example
  6. Take a second memory snapshot using Safari and look for the number of PanGestureHandler

Expected behavior

Previous PanGestureHandler components and their children should be deallocated when their parent is deallocated or no longer attached to any parent. This is the behavior of the example project without modifications.

Actual behavior

All PanGestureHandler components and their children stay in memory until application is killed.

Screenshots

Safari Dev Tools, list of new PanGestureHandler in memory since initial snapshot. Screen Shot 2020-11-30 at 2 34 55 PM

Detail of one retained PanGestureHandler Screen Shot 2020-11-30 at 2 35 07 PM

Snack or minimal code example

Edited part of SwipeableList.js (see comments):

function ListItem({ item, onRemove }) {
  const isRemoving = useSharedValue(false);
  const translateX = useSharedValue(0);
  // Declare stateful value
  const [isSwiping, setIsSwiping] = useState(false);

  const handler = useAnimatedGestureHandler({
    onStart: (evt, ctx) => {
      ctx.startX = translateX.value;
      // Modify stateful value from worklet
      runOnJS(setIsSwiping)(true);
    },

    onActive: (evt, ctx) => {
      const nextTranslate = evt.translationX + ctx.startX;
      translateX.value = Math.min(0, Math.max(nextTranslate, MAX_TRANSLATE));
    },

    onEnd: (evt) => {
      if (evt.velocityX < -20) {
        translateX.value = withSpring(
          MAX_TRANSLATE,
          springConfig(evt.velocityX)
        );
      } else {
        translateX.value = withSpring(0, springConfig(evt.velocityX));
      }
    },
  });
 
// ... Rest of ListItem implementation. Truncated for clarity
}

I can provide a project on Github if it helps.

Package versions

  • React: 16.13.1
  • React Native: 0.63.0
  • React Native Reanimated: Master @ 29491efc1851df320c2fa1ba90b5c217bd7a22cc (Nov 30th)
  • react-native-gesture-handler: 1.8.0
  • NodeJS: 12.16.1

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:5
  • Comments:16 (9 by maintainers)

github_iconTop GitHub Comments

2reactions
rrebasecommented, May 5, 2021

🏓 Any updates or workaround for this? It seems like the cause of leak is different after latest improvements. Maybe open a separate issue for discussing it? Having useAnimatedGestureHandler in a flatlist with a few timing worklets makes the leak a big issue.

Just want to note that you guys are doing amazing work here 💪 Would totally look into this myself, but don’t have the cpp experience.

2reactions
karol-bisztygacommented, Dec 11, 2020

Today I discovered that it could just be a problem of the gesture handler itself. Such code leaks memory:

import React from 'react';
import { View, Text } from 'react-native';
import { PanGestureHandler } from 'react-native-gesture-handler';

export default function App() {

  const handler = (e) => {};

  return (
    <View>
      <PanGestureHandler activeOffsetX={[-10, 10]} onGestureEvent={handler}>
        <Text>AAA</Text>
      </PanGestureHandler>
    </View>
  );
}

It has nothing to do with reanimated.

panghleaks

We will let you know when we check the GH.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How does a state update on an unmounted component cause ...
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your...
Read more >
How To Fix Memory Leak Issue In React Js Using Hook
Well, the main reason for the memory leak issue is updating the state endlessly. Infinite prefetching loops: This can happen if you're doing...
Read more >
Prevent Memory Leaks with componentWillUnmount() (How To)
Prevent Memory Leaks with componentWillUnmount() ... Since components do not always stay in the DOM, React also provides the componentWillUnmount ...
Read more >
The instructions for rendering a react component in column ...
Forum Thread - The instructions for rendering a react component in column cause a memory leak - React - EJ 2.
Read more >
[Solved]-React Component with Memory Leak-Reactjs
If you're using eventListeners , setInterval or other functions that needs to be cleaned, put them in componentDidMount . The server will not...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found