Getting Warning: Can't perform a React state update on an unmounted component

Jaskaran Singh

I am getting this error twice every time I run this. I did my search and tried to cancel the subscription of onAuthStateChanged() to useEffect() in the cleanup function, but still got the same error. Also, The uploadAndCreate() function is working perfectly fine but createPlan() is being called even before createPlan() finishes because of which I am getting undefined for fileUrl.

import React, { useState, useEffect } from "react";
import {
    Image,
    StyleSheet,
    Text,
    TextInput,
    TouchableOpacity,
    View,
} from "react-native";
import firebase from "firebase/app";
import "firebase/auth";
import "firebase/storage";
import { Link, Redirect } from "react-router-native";
import { getDocumentAsync } from "expo-document-picker";
import plus from "../../assets/plus.png";
import cross from "../../assets/cross.png";

const NewPlan = (props) => {
    const [planName, setPlanName] = useState();
    const [categoryName, setCategoryName] = useState();
    const [isPlanCreated, setIsPlanCreated] = useState(false);
    const [currUserId, setCurrUserId] = useState();
    const [uploadProgress, setUploadProgress] = useState(0);
    const [blob, setBlob] = useState({});
    const [isPlanNameValid, setIsPlanNameValid] = useState(true);
    const [ref, setRef] = useState();
    const [fileUrl, setFileUrl] = useState();

    useEffect(() => {
        let mounted = true;

        if (mounted) {
            firebase.auth().onAuthStateChanged((user) => {
                if (user) {
                    setCurrUserId(user.uid);
                }
            });
        }

        return () => {
            mounted = false;
        };
    }, []);

    const validate = () => {
        if (planName) {
            setIsPlanNameValid(true);
            uploadAndCreate();
        } else {
            setIsPlanNameValid(false);
        }
    };

    const uploadAndCreate = async () => {
        if (Object.keys(blob).length !== 0) {
            let task = ref.put(blob);

            await task.on(
                "state_changed",
                (snapshot) => {
                    let progress =
                        (snapshot.bytesTransferred / snapshot.totalBytes) * 100;

                    if (firebase.storage.TaskState.RUNNING)
                        setUploadProgress(progress);
                },
                (error) => {
                    console.log("Error uploading file: " + error);
                },
                () => {
                    task.snapshot.ref.getDownloadURL().then((downloadURL) => {
                        setFileUrl(downloadURL);
                    });
                }
            );
        }

        createPlan();
        setIsPlanCreated(true);
    };

    const createPlan = () => {
        let plansRef = firebase.database().ref().child("plans");
        let newPlanRef = plansRef.push();

        newPlanRef.set({
            plan_id: newPlanRef.key,
            plan_name: planName,
            created_by: currUserId,
            project_id: props.match.params.id,
            status: "active",
            category: categoryName || "uncategorized",
            file_url: fileUrl || "",
        });
    };

    const handlePlanName = (_planName) => setPlanName(_planName);

    return isPlanCreated ? (
        <Redirect to={"/project/" + props.match.params.id} />
    ) : (
        <View style={styles.container}>
            <Link
                to={"/project/" + props.match.params.id}
                style={styles.crossContainer}
            >
                <Image source={cross} style={styles.cross} />
            </Link>
            <View style={styles.topArea}>
                <Image source={plus} style={styles.plus} />
                <Text style={styles.title}>New Plan</Text>
                {!isPlanNameValid ? (
                    <Text style={styles.errorText}>
                        Please enter a valid name.
                    </Text>
                ) : null}
                <TextInput
                    placeholder="Plan Name"
                    style={styles.input}
                    placeholderTextColor="#fff"
                    onChangeText={handlePlanName}
                />
                {uploadProgress > 0 && uploadProgress < 100 ? (
                    <Text>Upload Progress: {uploadProgress.toFixed(0)}%</Text>
                ) : null}
                {uploadProgress === 100 ? <Text>Upload Complete</Text> : null}
                <TouchableOpacity
                    style={styles.button}
                    onPress={() => {
                        getDocumentAsync().then(async (response) => {
                            try {
                                let storageRef = firebase.storage().ref();
                                setRef(
                                    storageRef.child(
                                        response.uri.split("/")[14]
                                    )
                                );

                                let fetchResponse = await fetch(response.uri);
                                let blob = await fetchResponse.blob();

                                setBlob(blob);
                            } catch (error) {
                                console.log(error.message);
                            }
                        });
                    }}
                >
                    <Text style={styles.buttonText}>Upload Plan</Text>
                </TouchableOpacity>
            </View>
            <TouchableOpacity
                style={styles.bottomArea}
                onPress={() => {
                    validate();
                }}
            >
                <Text style={styles.createText}>Create</Text>
            </TouchableOpacity>
        </View>
    );
};

const styles = StyleSheet.create({
    // -- STYLES --
});

export default NewPlan;
CertainPerformance

Your mounted variable doesn't do anything. The only place it's checked is at the beginning of the useEffect, where it's definitely true.

While you could check it inside the onAuthStateChanged callback:

        firebase.auth().onAuthStateChanged((user) => {
            if (mounted && user) {
                setCurrUserId(user.uid);
            }
        });

It would be better to use the unsubscribe function returned by firebase:

useEffect(() => firebase.auth().onAuthStateChanged((user) => {
    if (user) {
        setCurrUserId(user.uid);
    }
}), []);

Or, de-sugared:

useEffect(() => {
    const unsubscribe = firebase.auth().onAuthStateChanged((user) => {
        if (user) {
            setCurrUserId(user.uid);
        }
    });
    return unsubscribe;
}, []);

It looks like you have multiple asynchronous actions that may be going on at the time that the component gets unmounted, which can't really be canceled easily. You can add a ref that indicates whether the component is currently mounted or not, and check that before calling any of the setter functions:

const mountedRef = useRef(true);
useEffect(() => {
    const unsubscribe = firebase.auth().onAuthStateChanged((user) => {
        if (user) {
            setCurrUserId(user.uid);
        }
    });
    return () => {
        mountedRef.current = false;
        unsubscribe();
    }
}, []);

and then, eg, instead of

setBlob(blob);

do

if (mountedRef.current) {
    setBlob(blob);
};

and follow the same pattern wherever there's a setter that might have run asynchronously.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

Warning: Can't perform a React state update on an unmounted component

I am getting the error message Warning: Can't perform a React state update on an unmounted component

Can't get rid of: Warning: Can't perform a React state update on an unmounted component

Warning: Can't perform a React state update on an unmounted component. In a functional component

React-Native: Warning: Can't perform a React state update on an unmounted component

How to fix Can't perform a React state update on an unmounted component warning in reactjs

how to remove the warning "can't perform a react state update on unMounted Component"

Google Analytics: Warning: Can't perform a React state update on an unmounted component

Warning: Can't perform a React state update on an unmounted component, in SceneView (created by Pager)

Warning: Can't perform a React state update on an unmounted component. useEffect cleanup function

How to fix: Warning: Can't perform a React state update on an unmounted component

React Can't perform a React state update on an unmounted component error

Can't perform a React state update on an unmounted component in react

React - Can't perform a React state update on an unmounted component

React Spring "Can't perform a React state update on an unmounted component"

React: Can't perform a React state update on an unmounted component

Can't perform a React state update on an unmounted component theme provider

can't perform a react state update on an unmounted component issue with useEffect

useEffect - Can't perform a React state update on an unmounted component

React can't perform state update on unmounted component

Can't perform a React state update on an unmounted component in reactjs

Can't perform a React state update on an unmounted component

Can't perform a React State update on unMounted child component?

Can't perform a react state update on an unmounted component error

React form submit and "can't perform state update on an unmounted component"?

ReactJS & Redux: Can't perform a React state update on an unmounted component

Can't perform a React state update on an unmounted component?

Fix "Can't perform a React state update on an unmounted component" error

ReactJS : Can't perform a React state update on an unmounted component