Matt
Matt

Reputation: 115

Warning: Cant perform React state update

I am creating a basic sign in / Sign out app utilizing Firebase. When I boot the app and sign up a user, everything seems fine. But when I log out and attempt to sign up a second user I get the yellow warning below. I have read about how to avoid this warning by using isMounted() however I have not gotten it to work and Ive read that isMounted() is deprecated anyway. Please help. Thank you!

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 application. To fix, cancel all subscriptions and asynchronous tasks in %s.%s, the componentWillUnmount method, 
    in HomeScreen (at SceneView.js:9)
    in SceneView (at StackViewLayout.tsx:900)
    in RCTView (at createAnimatedComponent.js:151)
    in AnimatedComponent (at StackViewCard.tsx:106)
    in RCTView (at createAnimatedComponent.js:151)
    in AnimatedComponent (at screens.native.js:71)
    in Screen (at StackViewCard.tsx:93)
    in Card (at createPointerEventsContainer.tsx:95)
    in Container (at StackViewLayout.tsx:975)

From the warning, I believe my problem is in my HomeScreen.js below:

import React from 'react';
import { Text, Button, View, StyleSheet, Image } from 'react-native'
import * as firebase from 'firebase';

export default class HomeScreen extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            name: "",
            email: ""
        }
    }
    static navigationOptions = {
        title: "Home",
        header: null
    }
    componentDidMount() {
        firebase.auth().onAuthStateChanged(authenticate => {
            if (authenticate) {
                this.setState({
                    email: authenticate.email,
                    name: authenticate.displayName
                });
            } else {
                this.props.navigation.replace("SignIn");
            }
        });
    }

    signOutUser = () => {
        firebase
            .auth()
            .signOut()
            .then(() => console.log("signout"))
            .catch(error => alert(error.message))
    }

    render() {
        return (
            <View style={styles.container}>
                <View style={styles.logoContainer}>
                    <Image source={require('../assets/logo.png')} />
                    <Text>Home Screen</Text>
                </View>
                <View>
                    <Text>Hey {this.state.name}</Text>
                    <Text>Logged in as {this.state.email}</Text>

                </View>
                <Button
                    style={styles.button}
                    title="Log Out"
                    full
                    rounded
                    success
                    onPress={() => {
                        this.signOutUser();
                    }}
                >
                    <Text style={styles.buttonText}>Sign Out</Text>
                </Button>
            </View >

        );
    }
}

Thanks again! -Matt

Upvotes: 0

Views: 593

Answers (2)

LogaKrishnan
LogaKrishnan

Reputation: 501

You need to unsubscribe the firebase listener, or else it will lead to this memory leakage issue.

import React from 'react';
import { Text, Button, View, StyleSheet, Image } from 'react-native'
import * as firebase from 'firebase';

export default class HomeScreen extends React.Component {
    unsubscribeUserAuthStateChangedListener = null;

    constructor(props) {
        super(props);
        this.state = {
            name: "",
            email: ""
        }
    }
    static navigationOptions = {
        title: "Home",
        header: null
    }
    componentDidMount() {
        this.unsubscribeUserAuthStateChangedListener = firebase.auth().onAuthStateChanged(authenticate => {
            if (authenticate) {
                this.setState({
                    email: authenticate.email,
                    name: authenticate.displayName
                });
            } else {
                this.props.navigation.replace("SignIn");
            }
        });
    }

    componentWillUnmount() {
      if (this.unsubscribeUserAuthStateChangedListener) {
        this.unsubscribeUserAuthStateChangedListener();
      }
    }

    signOutUser = () => {
        firebase
            .auth()
            .signOut()
            .then(() => console.log("signout"))
            .catch(error => alert(error.message))
    }

    render() {
        return (
            <View style={styles.container}>
                <View style={styles.logoContainer}>
                    <Image source={require('../assets/logo.png')} />
                    <Text>Home Screen</Text>
                </View>
                <View>
                    <Text>Hey {this.state.name}</Text>
                    <Text>Logged in as {this.state.email}</Text>

                </View>
                <Button
                    style={styles.button}
                    title="Log Out"
                    full
                    rounded
                    success
                    onPress={() => {
                        this.signOutUser();
                    }}
                >
                    <Text style={styles.buttonText}>Sign Out</Text>
                </Button>
            </View >

        );
    }
}

Upvotes: 1

Peter Ola
Peter Ola

Reputation: 130

Self explanatory, Like the name unmounted, This happens when setState is called after the screen has been existed.

onAuthStateChanged is a listener and can be called at anytime, I'd advise you control this yourself and check if screen is mounted before setting state.

  constructor(props) {
            super(props);
            this.state = {
                name: "",
                email: "",
                isMounted: true
            }
        }




    componentDidMount() {
            firebase.auth().onAuthStateChanged(authenticate => {
                if (authenticate) {
                  if(this.state.isMounted) {
                    this.setState({
                        email: authenticate.email,
                        name: authenticate.displayName
                    });
                 }
                } else {
                    this.props.navigation.replace("SignIn");
                }
            });
        }

    componentwillUnMount() {
    this.setState({isMounted: false})
    }

Upvotes: 0

Related Questions