Christopher Mellor
Christopher Mellor

Reputation: 444

Animate on removal from dom

What is the correct way to do animations when removing something from the DOM?

It seems like no matter what I do, when I remove something form the dom, it doesn't trigger a re render until the items are removed and then it snaps away out of view instead of transitioning before it removes the data. Here is an example of something I've tried.

  const [isExpanded, setIsExpanded] = useState(false);
  const [isVisible, setIsVisible] = useState(false);

  const toggleReplies = () => {
    LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);

    if (isExpanded) {
      setIsVisible(false);
      setTimeout(() => {
        setIsExpanded(false);
      }, 300);
    } else {
      setIsExpanded(true);
      setIsVisible(true);
    }
  };

  return (
    <View style={styles.container}>
      <Button title="Toggle Replies" onPress={toggleReplies} />
      {isVisible && (
        <View style={styles.replyBox}>
          <Text>This is a reply!</Text>
        </View>
      )}
    </View>
  );
};

Upvotes: 1

Views: 385

Answers (4)

Cafer Y&#252;kseloğlu
Cafer Y&#252;kseloğlu

Reputation: 907

Christopher.

Animating elements out upon removing them from the DOM can be complex because React removes the element without any further ado, and for that reason, the element simply "pops" out and doesn't animate away as expected. There are two solid approaches to fix this:

Solution Using Animated API:

  1. Import Animated and useRef:

    import React, { useState, useRef } from 'react';
    import { View, Text, Button, Animated, StyleSheet } from 'react-native';
    
  2. Setup Animation:

    const MyComponent = () => {
      const [isVisible, setIsVisible] = useState(true);
      const fadeAnim = useRef(new Animated.Value(1)).current; // Initial opacity value
    
      const toggleReplies = () => {
        if (isVisible) {
          // Fade out animation
          Animated.timing(fadeAnim, {
            toValue: 0,
            duration: 300,
            useNativeDriver: true,
          }).start(() => {
            setIsVisible(false); // Hide component after fade out
          });
        } else {
          setIsVisible(true); // Show component
          // Reset opacity to 1 before fade in
          fadeAnim.setValue(0);
          // Fade in animation
          Animated.timing(fadeAnim, {
            toValue: 1,
            duration: 300,
            useNativeDriver: true,
          }).start();
        }
      };
    
      return (
        <View style={styles.container}>
          <Button title="Toggle Replies" onPress={toggleReplies} />
          {isVisible && (
            <Animated.View style={{ ...styles.replyBox, opacity: fadeAnim }}>
              <Text>This is a reply!</Text>
            </Animated.View>
          )}
        </View>
      );
    };
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
      },
      replyBox: {
        padding: 20,
        backgroundColor: 'lightgray',
        marginTop: 10,
      },
    });
    
    export default MyComponent;
    

Explanation:

  1. Animated API: This is used to create fade in and fade out animations.
  2. toggleReplies Function: Controls the visibility of the component and triggers the animations.
  3. Conditional Rendering: The component is conditionally rendered based on isVisible state.

Additional Tips:

  • Ensure you use useNativeDriver: true for better performance.
  • Adjust the duration value to control the speed of the animation.

This method provides a smooth transition effect when showing and hiding components in React Native.

Hope this helps! Let me know if you have any more questions.

Upvotes: -1

AmerllicA
AmerllicA

Reputation: 32572

I don't know what exactly you want to do with fading out, but definitely, your solution is animation, I think the following codes can help you reach your goal:

import React from 'react';
import type {FC, ReactNode} from 'react';
import Animated, {
  FadeInUp,
  FadeOutUp,
  useAnimatedStyle,
  useSharedValue,
} from 'react-native-reanimated';


interface FadeViewProps {
  id: string | number; // <== pass a unique id here
  children?: ReactNode;
}

const FadeView: FC<FadeViewProps> = ({ id, children }) => {
  const opacity = useSharedValue(1);

  const wrapperAnimantedStype = useAnimatedStyle(() => {
    opacity: opacity.value,
  });

  return (
    <Animated.View
      key={id}
      entering={FadeInUp}
      exiting={FadeOutUp}
      style={[wrapperAnimantedStype]}
    >
     {children}
    </Animated.View>

When you try to render it or remove it from UI it will show you a fade-out animation. You can get inspired by it to improve your exact desire.

Upvotes: 1

Umair Ahmed
Umair Ahmed

Reputation: 587

I've seen a bug before where you have to move your Layoutanimation.Confi.. outside the function call. Maybe in a useEffect or just in the render of the function after initialisation and see if that works.

For Layout Animation to work on android, I you have to enable setLayoutAnimationEnabledExperimental.

I'm assuming you are already using React Navigation, so you will have React Native Reanimated already available in your project. You can start by adding it in your package.json if not already present. Then you can use it to do enter/exit Animations quite easily.

Upvotes: 0

Luke Celitan
Luke Celitan

Reputation: 340

This is a common problem for animating the react components and elements, react is creating and destroying the dom nodes, so the browser does not apply any styles to elements that are non existing.

2 options to achieve what you want, just change the styles and keep the nodes in dom, or you can use https://reactcommunity.org/react-transition-group/css-transition for adding "exit", "enter" styles to elements just before they will get destroyed / created.

Upvotes: 0

Related Questions