Search code examples
reactjsreact-nativereact-propslottiereact-native-component

React Native componentDidUpdate props not connecting to main component?


When trying to add a nice Lottie animation to my Login component, I got stuck because the props inside componentDidUpdate don't seem to be recognised when referenced on the main component file.

'isActive' from 'this.props.isActive'(the Lottie component) and 'isActive' from ''(the main component file) should connect and trigger the animation, but somehow they don't.

The Lottie component code

import React from "react";
import styled from "styled-components";
import LottieView from "lottie-react-native";
import { Animated, Dimensions } from "react-native";

const ScreenHeight = Dimensions.get("window").height;

class Success extends React.Component {
  state = {
    top: new Animated.Value(0),
    opacity: new Animated.Value(0),
  };

  componentDidMount() {}

  componentDidUpdate() {
    if (this.props.isActive) {
      Animated.timing(this.state.top, { toValue: 0, duration: 0 }).start();
      Animated.timing(this.state.opacity, { toValue: 1 }).start();

      this.animation.play();
    }
  }
  render() {
    return (
      <AnimatedContainer
        style={{ top: this.state.top, opacity: this.state.opacity }}
      >
        <LottieView
          source={require("../assets/checked-done.json")}
          autoPlay={false}
          loop={false}
          ref={(animation) => {
            this.animation = animation;
          }}
        />
      </AnimatedContainer>
    );
  }
}

export default Success;

const Container = styled.View`
  width: 100%;
  height: 100%;
  background: rgba(255, 255, 255, 0.9);
  justify-content: center;
  align-items: center;
  position: absolute;
  top: 0;
  left: 0;
`;

And the main login component I am trying to connect it to:

import React from "react";
import styled from "styled-components";
import {
  TouchableOpacity,
  TouchableWithoutFeedback,
  Keyboard,
} from "react-native";
import Success from "./Success";

class ModalLogin extends React.Component {
  state = {
    email: "",
    password: "",
    isSuccessful: false,
  };

  handleLogin = () => {
    console.log(this.state.email, this.state.password);
  };

  tapBackground = () => {
    Keyboard.dismiss();
  };

  render() {
    return (
      <TouchableWithoutFeedback onPress={this.tapBackground}>
        <Container
          style={{ backgroundColor: "grey" }}
          ref={(n) => {
            if (n && viewRef === null) {
              setViewRef(findNodeHandle(n));
            }
          }}
        >
          <Modal>
            <Logo source={require("../assets/logo.png")} />
            <Text>Manage your lessons</Text>
            <TextInput
              onChangeText={(email) => this.setState({ email })}
              placeholder="Email"
              keyboardType="email-address"
            />
            <TextInput
              onChangeText={(password) => this.setState({ password })}
              placeholder="Password"
              secureTextEntry={true}
            />
            <IconEmail source={require("../assets/icon-email.png")} />
            <IconPassword source={require("../assets/icon-password.png")} />
            <TouchableOpacity onPress={this.handleLogin}>
              <Button>
                <ButtonText>Login</ButtonText>
              </Button>
            </TouchableOpacity>
          </Modal>
          <Success isActive={true} />
        </Container>
      </TouchableWithoutFeedback>
    );
  }
}

export default ModalLogin;

const viewRef = styled.View``;

const Container = styled.View`
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.75);
  justify-content: center;
  align-items: center;
`;
const Modal = styled.View`
  width: 335px;
  height: 370px;
  background: white;
  border-radius: 20px;
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
  align-items: center;
`;

const Logo = styled.Image`
  width: 90px;
  height: 90px;
  margin-top: 8px;
  margin-bottom: -8px;
`;
const Text = styled.Text`
  margin-top: 20px;
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  width: 160px;
  text-align: center;
  color: #b8bece;
`;
const TextInput = styled.TextInput`
  border: 1px solid #dbdfea;
  width: 295px;
  height: 44px;
  border-radius: 10px;
  font-size: 17px;
  color: #3c4560;
  margin-top: 20px;
  padding-left: 44px;
`;
const Button = styled.View`
  background: #8dd2dc;
  width: 295px;
  height: 50px;
  justify-content: center;
  align-items: center;
  border-radius: 10px;
  margin-top: 20px;
`;

const ButtonText = styled.Text`
  color: white;
  font-weight: 600;
  text-transform: uppercase;
`;

const IconEmail = styled.Image`
  width: 30px;
  height: 30px;
  position: absolute;
  top: 174px;
  left: 31px;
`;

const IconPassword = styled.Image`
  width: 23px;
  height: 23px;
  position: absolute;
  top: 241px;
  left: 35px;
`;

Also Package.json

  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "web": "expo start --web",
    "eject": "expo eject"
  },
  "dependencies": {
    "@react-native-community/masked-view": "0.1.6",
    "@react-navigation/native": "^5.4.3",
    "expo": "~37.0.3",
    "lodash": "^4.17.15",
    "lottie-react-native": "^3.4.0",
    "native-base": "^2.13.12",
    "react": "~16.9.0",
    "react-dom": "~16.9.0",
    "react-native": "https://github.com/expo/react-native/archive/sdk-37.0.1.tar.gz",
    "react-native-blur": "ndbroadbent/react-native-blur#android-fix",
    "react-native-calendars": "^1.265.0",
    "react-native-gesture-handler": "~1.6.0",
    "react-native-i18n": "^1.0.0",
    "react-native-material-design": "^0.3.7",
    "react-native-reanimated": "~1.7.0",
    "react-native-safe-area-context": "0.7.3",
    "react-native-screens": "~2.2.0",
    "react-native-web": "~0.11.7",
    "react-navigation": "^4.3.9",
    "react-navigation-stack": "^2.5.1",
    "react-navigation-tabs": "^2.8.13",
    "react-redux": "^7.2.0",
    "redux": "^4.0.5",
    "styled-components": "^5.1.0"
  },
  "devDependencies": {
    "@babel/core": "^7.8.6",
    "babel-preset-expo": "~8.1.0"
  },
  "private": true

Solution

  • componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render. https://reactjs.org/docs/react-component.html#componentdidupdate

    componentDidUpdate isn't called on the initial render. So if the isActive prop starts as true and doesn't change then it will not trigger your animation.

    <Success isActive={true} />
    

    If you want your animation to play on the first render then you will need to also trigger the animation in componentDidMount.

    Or you could refactor to use a function component and hooks.

    useEffect(()=> {
      // animation code
    }, [isActive])