Sunday, June 16, 2019

Dynamically Add / Remove Component with Animation in React Native

This tutorial explains how to add and remove component with animation in react native application. In this example we are going to create add and remove functionality inside the scrollview. In order to use these functionalities in side the scrollview, you have to perform below operation :
1. When user click on add button, then it will add new entry inside the scrollview.
2. When user click on delete button, then it remove that particular enter from the scrollview.

Dynamically Add / Remove Component with Animation in React Native
  Download Now

React Native Dynamically Add / Remove Component with Animation :

Lets see the complete source code App.js component that helps to dynamically add and remove component with animation in react native application.

Video Demo : 





project structure :
Dynamically Add / Remove Component with Animation in React Native

Step 1: Create a new react native project, if you don’t know how to create a new project in react native just follow this tutorial.

Step 2: Create a new Item component inside the src folder. This component helps to add and remove content inside the scrollview with animation effect.

Step 3: Through react , react-native  packages import all required components.
import React, { Component } from 'react';
import {  Animated, Text, TouchableOpacity, StyleSheet,Image, Dimensions, Platform,  UIManager } from 'react-native';

Step 4:  Define constant named as width, which capture the width of the device automatically with the help of Dimension component.
const width = Dimensions.get('window').width;

Step 5: Lets create constructor block inside your Item component.
constructor() {
    super();
    this.animatedValue = new Animated.Value(0);

    if (Platform.OS === 'android') {
      UIManager.setLayoutAnimationEnabledExperimental(true);
    }
  }

Step 6: Lets implement shouldComponentUpdate method. Basically, shouldComponentUpdate  is a lifecycle method of react and used to improve the performance of the app. shouldComponentUpdate  method returns a boolean  either true  or false . If it returns true then component re-renders otherwise react skips the re-rendering process.
 shouldComponentUpdate(nextProps, nextState) {
    if (nextProps.item.id !== this.props.item.id) {
      return true;
    }
    return false;
  }

Step 7: Lets implement componentDidMount method. This is the place where our animation will work for adding a component and after completing the animation I am calling afterAnimationComplete  method which is received as prop from parent component.
componentDidMount() {
    Animated.timing(
      this.animatedValue,
      {
        toValue: 0.5,
        duration: 500,
        useNativeDriver: true
      }
    ).start(() => {
      this.props.afterAnimationComplete();
    });
  }

Step 8: Lets create removeItem function that helps to execute the removing animation and again I am calling removeItem  method to actually remove the selected item from ScrollView  and this method is also received as prop from parent component.
removeItem = () => {
    Animated.timing(
      this.animatedValue,
      {
        toValue: 1,
        duration: 500,
        useNativeDriver: true
      }
    ).start(() => {
      this.props.removeItem(this.props.item.id);
    });
  }

Step 9: Implement render method inside the App class and wrapped the below layout design inside the root View component. when we add a new component then element’s translateX  position is equal to the -width  and component animates from -width  to   as you can see in the translateAnimation ‘s outputRange  and when user clicks on the remove icon on any component then component animates from   to width  (as you can see in the translateAnimation ‘s outputRange) and opacityAnimation  also works along with translateAnimation .
render() {
    const translateAnimation = this.animatedValue.interpolate({
      inputRange: [0, 0.5, 1],
      outputRange: [-width, 0, width]
    });

    const opacityAnimation = this.animatedValue.interpolate({
      inputRange: [0, 0.5, 1],
      outputRange: [0, 1, 0]
    });

    return (
      <Animated.View style={[
        styles.viewHolder, {
          transform: [{ translateX: translateAnimation }],
          opacity: opacityAnimation
        }]}
      >
        <Text
          style={styles.displayText}>
          Row Now :  {this.props.item.text}
        </Text>
        <TouchableOpacity
          style={styles.removeBtn}
          onPress={this.removeItem}
        >
          <Image
            source={require('../../images/deleteButton.png')}
            style={styles.btnImage}
          />
        </TouchableOpacity>
      </Animated.View>
    );
  }

Step 10 : Apply the below style sheet design. 
const styles = StyleSheet.create(
  {
    viewHolder: {
      paddingVertical: 15,
      backgroundColor: '#2196f3',
      justifyContent: 'center',
      alignItems: 'flex-start',
      margin: 4,
      paddingLeft: 15,
      borderRadius: 10
    },
    displayText: {
      color: 'white',
      fontSize: 25,
      paddingRight: 17
    },
    removeBtn: {
      position: 'absolute',
      right: 13,
      width: 30,
      height: 30,
      borderRadius: 15,
      justifyContent: 'center',
      alignItems: 'center',
      backgroundColor: 'white'
    },
    btnImage: {
      resizeMode: 'contain',
      width: '100%',
    },
  });

Lets see the below complete source code for Item component.

Item.js
import React, { Component } from 'react';
import {  Animated, Text, TouchableOpacity, StyleSheet,Image, Dimensions, Platform,  UIManager } from 'react-native';


const width = Dimensions.get('window').width;

export default class Item extends Component {

  constructor() {
    super();
    this.animatedValue = new Animated.Value(0);

    if (Platform.OS === 'android') {
      UIManager.setLayoutAnimationEnabledExperimental(true);
    }
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (nextProps.item.id !== this.props.item.id) {
      return true;
    }
    return false;
  }

  componentDidMount() {
    Animated.timing(
      this.animatedValue,
      {
        toValue: 0.5,
        duration: 500,
        useNativeDriver: true
      }
    ).start(() => {
      this.props.afterAnimationComplete();
    });
  }

  removeItem = () => {
    Animated.timing(
      this.animatedValue,
      {
        toValue: 1,
        duration: 500,
        useNativeDriver: true
      }
    ).start(() => {
      this.props.removeItem(this.props.item.id);
    });
  }

  render() {
    const translateAnimation = this.animatedValue.interpolate({
      inputRange: [0, 0.5, 1],
      outputRange: [-width, 0, width]
    });

    const opacityAnimation = this.animatedValue.interpolate({
      inputRange: [0, 0.5, 1],
      outputRange: [0, 1, 0]
    });

    return (
      <Animated.View style={[
        styles.viewHolder, {
          transform: [{ translateX: translateAnimation }],
          opacity: opacityAnimation
        }]}
      >
        <Text
          style={styles.displayText}>
          Row Now :  {this.props.item.text}
        </Text>
        <TouchableOpacity
          style={styles.removeBtn}
          onPress={this.removeItem}
        >
          <Image
            source={require('../../images/deleteButton.png')}
            style={styles.btnImage}
          />
        </TouchableOpacity>
      </Animated.View>
    );
  }
}

const styles = StyleSheet.create(
  {
    viewHolder: {
      paddingVertical: 15,
      backgroundColor: '#2196f3',
      justifyContent: 'center',
      alignItems: 'flex-start',
      margin: 4,
      paddingLeft: 15,
      borderRadius: 10
    },
    displayText: {
      color: 'white',
      fontSize: 25,
      paddingRight: 17
    },
    removeBtn: {
      position: 'absolute',
      right: 13,
      width: 30,
      height: 30,
      borderRadius: 15,
      justifyContent: 'center',
      alignItems: 'center',
      backgroundColor: 'white'
    },
    btnImage: {
      resizeMode: 'contain',
      width: '100%',
    },
  });

Step 11: Open App.js File in your favorite code editor and erase all code and follow this tutorial.

Step 12: Through react , react-native  packages import all required components.
import React, { Component } from 'react';
import { AppRegistry,TouchableOpacity, View, StyleSheet, ScrollView, Image, LayoutAnimation, } from 'react-native';
import Item from './src/components/Item.js'

Step 13: Lets create constructor block inside your App component.
constructor() {
    super();
    this.state = { valueArray: [], disabled: false }
    this.addNewEle = false;
    this.index = 0;
  }

Step 14: Lets create afterAnimationComplete function. Remember, this method is called by Item  component after completing the adding component animation. In this method, I am going to set false the disabled state, which re-enables the add component (+) button and allows to add new component.
  afterAnimationComplete = () => {
    this.index += 1;
    this.setState({ disabled: false });
  }

Step 15: Lets create addMore function. This method is responsible to add a new object into array and disable the add button while running adding component animation.
addMore = () => {
    this.addNewEle = true;
    const newlyAddedValue = { id: "id_" + this.index, text: this.index + 1 };

    this.setState({
      disabled: true,
      valueArray: [...this.state.valueArray, newlyAddedValue]
    });
  }

Step 16: Lets create remove function. This method is responsible for removing the selected item from array and applying the LayoutAnimation  to re-arrange the remaining components with nice animation.
 remove(id) {
    this.addNewEle = false;
    const newArray = [...this.state.valueArray];
    newArray.splice(newArray.findIndex(ele => ele.id === id), 1);

    this.setState(() => {
      return {
        valueArray: newArray
      }
    }, () => {
      LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
    });
  }

Step 17: Implement render method inside the App class and wrapped the below layout design inside the root View component.
render() {
    return (
      <View style={styles.container} >
        <ScrollView
          ref={scrollView => this.scrollView = scrollView}
          onContentSizeChange={() => {
            this.addNewEle && this.scrollView.scrollToEnd();
          }}
        >
          <View style={{ flex: 1, padding: 4 }}>
            {this.state.valueArray.map(ele => {
              return (
                <Item
                  key={ele.id}
                  item={ele}
                  removeItem={(id) => this.remove(id)}
                  afterAnimationComplete={this.afterAnimationComplete}
                />
              )
            })}
          </View>
        </ScrollView>

        <TouchableOpacity
          activeOpacity={0.8}
          style={styles.addBtn}
          disabled={this.state.disabled}
          onPress={this.addMore}
        >
          <Image source={require('./images/addButton.png')} style={styles.btnImage} />
        </TouchableOpacity>
      </View>
    );
  }

Step 18 : Apply the below style sheet design. 
const styles = StyleSheet.create(
  {
    container: {
      flex: 1,
    },
    addBtn: {
      position: 'absolute',
      right: 25,
      bottom: 25,
      width: 70,
      height: 70,
      justifyContent: 'center',
      alignItems: 'center',
      borderRadius: 30,
      backgroundColor: 'white'
    },
    btnImage: {
      resizeMode: 'contain',
      width: '100%',

    },
  });

Lets see the complete source for App component in react native application

App.js
import React, { Component } from 'react';
import { AppRegistry,TouchableOpacity, View, StyleSheet, ScrollView, Image, LayoutAnimation, } from 'react-native';
import Item from './src/components/Item.js'

export default class App extends Component {

  constructor() {
    super();
    this.state = { valueArray: [], disabled: false }
    this.addNewEle = false;
    this.index = 0;
  }

  afterAnimationComplete = () => {
    this.index += 1;
    this.setState({ disabled: false });
  }

  addMore = () => {
    this.addNewEle = true;
    const newlyAddedValue = { id: "id_" + this.index, text: this.index + 1 };

    this.setState({
      disabled: true,
      valueArray: [...this.state.valueArray, newlyAddedValue]
    });
  }

  remove(id) {
    this.addNewEle = false;
    const newArray = [...this.state.valueArray];
    newArray.splice(newArray.findIndex(ele => ele.id === id), 1);

    this.setState(() => {
      return {
        valueArray: newArray
      }
    }, () => {
      LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
    });
  }
  
  render() {
    return (
      <View style={styles.container} >
        <ScrollView
          ref={scrollView => this.scrollView = scrollView}
          onContentSizeChange={() => {
            this.addNewEle && this.scrollView.scrollToEnd();
          }}
        >
          <View style={{ flex: 1, padding: 4 }}>
            {this.state.valueArray.map(ele => {
              return (
                <Item
                  key={ele.id}
                  item={ele}
                  removeItem={(id) => this.remove(id)}
                  afterAnimationComplete={this.afterAnimationComplete}
                />
              )
            })}
          </View>
        </ScrollView>

        <TouchableOpacity
          activeOpacity={0.8}
          style={styles.addBtn}
          disabled={this.state.disabled}
          onPress={this.addMore}
        >
          <Image source={require('./images/addButton.png')} style={styles.btnImage} />
        </TouchableOpacity>
      </View>
    );
  }
}

const styles = StyleSheet.create(
  {
    container: {
      flex: 1,
    },
    addBtn: {
      position: 'absolute',
      right: 25,
      bottom: 25,
      width: 70,
      height: 70,
      justifyContent: 'center',
      alignItems: 'center',
      borderRadius: 30,
      backgroundColor: 'white'
    },
    btnImage: {
      resizeMode: 'contain',
      width: '100%',

    },
  });

Screenshot :

Dynamically Add / Remove Component with Animation in React Native

Dynamically Add / Remove Component with Animation in React Native

Dynamically Add / Remove Component with Animation in React Native

Dynamically Add / Remove Component with Animation in React Native

This is all about Dynamically Add / Remove Component with Animation in React Native. Thank you for reading this article, and if you have any problem, have a another better useful solution about this article, please write message in the comment section.

2 comments: