React-native-reanimated: Error: Trying to remove a child that doesn't exist

Created on 16 Jun 2020  路  3Comments  路  Source: software-mansion/react-native-reanimated

Description

I am fading an Image out and once it has completed the fade out animation I unmount it by setting a state to false. Once the state has been set, the Animated component will get unmounted and the error/warning is thrown.

Screenshots

Screenshot GIF demo
Screenshot of error message GIF demo of fade out animation and error popping up

Steps To Reproduce

  1. Create an Animated component and conditionally render it (using renderMyComponent state, per default it's true) (I'm using <Blurhash> from react-native-blurhash)
  2. Use a timing animation to fade it out.
  3. Once the timing animation is complete, unmount the component by setting the state boolean renderMyComponent to false

Expected behavior

The component gets unmounted, memory gets freed and animation is gone.

Actual behavior

Component gets unmounted (I think), but the error Trying to remove a child that doesn't exist gets thrown.

Snack or minimal code example

const AnimatedBlurhash = Animated.createAnimatedComponent(Blurhash);
// ...
const [renderBlurhash, setRenderBlurhash] = useState(true);
const blurhashOpacity = useValue(0);  // from redash, shoutout @wcandillon 
// ...
const onImageLoadEnd = useCallback(() => {
  Reanimated.timing(blurhashOpacity, {
    duration: 300,
    toValue: 0,
    easing: Easing.linear,
  }).start(() => {
    setRenderBlurhash(false);
  });
}, [blurhashOpacity]);
// ...
return (
  <View> 
    ...
    {renderBlurhash && (<AnimatedBlurhash style={{ opacity: blurhashOpacity }} ... />)}
  </View>
  );

Package versions

  • React: 16.13.1
  • React Native: 0.62.2
  • React Native Reanimated: ^1.9.0
  • react-native-blurhash: ^1.0.7
馃悶 Bug 馃コcan-repro

Most helpful comment

Minimal repro:

import React, {useState} from 'react';
import {SafeAreaView, Button} from 'react-native';
import Animated, {timing, Value, Easing} from 'react-native-reanimated';

export default function Example() {
  const [showRect, setShowRect] = useState(true);

  const opacity = new Value(1);
  const t = () =>
    timing(opacity, {
      duration: 300,
      toValue: 0,
      easing: Easing.linear,
    }).start(() => {
      setShowRect(false);
    });

  return (
    <SafeAreaView style={{alignItems: 'center'}}>
      <Button onPress={t} title="Opacity" />
      {showRect && (
        <Animated.View
          style={{width: 100, height: 100, backgroundColor: 'green', opacity}}
        />
      )}
    </SafeAreaView>
  );
}

All 3 comments

When I wait a short amount of time, e.g. 500ms before setting the boolean state renderBlurhash to false, the error is not thrown. Probably has to do with the async nature of react native, meaning the animation is still not fully disposed in the finished callback which makes immediate unmounting impossible.

Minimal repro:

import React, {useState} from 'react';
import {SafeAreaView, Button} from 'react-native';
import Animated, {timing, Value, Easing} from 'react-native-reanimated';

export default function Example() {
  const [showRect, setShowRect] = useState(true);

  const opacity = new Value(1);
  const t = () =>
    timing(opacity, {
      duration: 300,
      toValue: 0,
      easing: Easing.linear,
    }).start(() => {
      setShowRect(false);
    });

  return (
    <SafeAreaView style={{alignItems: 'center'}}>
      <Button onPress={t} title="Opacity" />
      {showRect && (
        <Animated.View
          style={{width: 100, height: 100, backgroundColor: 'green', opacity}}
        />
      )}
    </SafeAreaView>
  );
}

When I wait a short amount of time, e.g. 500ms before setting the boolean state renderBlurhash to false, the error is not thrown. Probably has to do with the async nature of react native, meaning the animation is still not fully disposed in the finished callback which makes immediate unmounting impossible.

The case here is that createAnimatedComponent creates a wrapper that manages the lifecycle of nodes in Reanimated. In componentWillUnmount it detaches nodes from the native side. Backward compatible API of Reanimated also try to detach Value passed as the first argument, but does it after the wrapper has unmounted - it tries detaching detached value resulting in this warning.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

colinux picture colinux  路  3Comments

afctemka picture afctemka  路  3Comments

wasim-abuzaher picture wasim-abuzaher  路  3Comments

alexfov picture alexfov  路  3Comments

dinhmai74 picture dinhmai74  路  3Comments