4

I'm new to react native and was following a tutorial on medium about how to connect with firebase auth. Everything seems to work fine, but I keep getting this warning below:

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 a useEffect cleanup function.

I pretty much did exactly what was said in the tutorial and tried out a few other things to fix it, but nothing seems to work. Here is the code it's pointing the error to:

    let currentUserUID = firebase.auth().currentUser.uid;

    const [firstName, setFirstName] = useState('');

    useEffect(() => { 
        getUserInfo();
      })

      async function getUserInfo(){
        try {
          let doc = await firebase
            .firestore()
            .collection('users')
            .doc(currentUserUID)
            .get();
  
          if (!doc.exists){
            Alert.alert('No user data found!')
          } else {
            let dataObj = doc.data();
            setFirstName(dataObj.firstName)
          }
        } catch (err){
        Alert.alert('There is an error.', err.message)
        }
      }

It would be great if anyone could help me fix this problem and explain what exactly has gone wrong.

This is the link to the tutorial I was following:

Sinan Yaman
  • 5,714
  • 2
  • 15
  • 35

2 Answers2

1

The issue here is that you are potentially enqueueing a state update after a component has unmounted. Since you are accessing your firestore directly, asynchronously, you can use a React ref to track if the component is still mounted before enqueuing the update.

const isMountedRef = React.ref(null);

useEffect(() => {
  isMountedRef.current = true;               // set true when mounted
  return () => isMountedRef.current = false; // clear when unmounted
}, []);

useEffect(() => {
  async function getUserInfo(){
    try {
      let doc = await firebase
        .firestore()
        .collection('users')
        .doc(currentUserUID)
        .get();

      if (!doc.exists){
        Alert.alert('No user data found!')
      } else {
        let dataObj = doc.data();
        if (isMountedRef.current) { // <-- check if still mounted
          setFirstName(dataObj.firstName);
        }
      }
    } catch (err){
      Alert.alert('There is an error.', err.message)
    }
  }
  getUserInfo();
}, []); // <-- include dependency array, empty to run once when mounting
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
0

Your getInfoUser() function is async. You should do something like this in useEffect:

useEffect(async () => { await getUserInfo(); }, [])

As a second argument in useEffect use the dependency array. Using the dependency array is equivalent with componentDidMount.

Your effect will take place only once when the component is first rendered. Additionally there are cases when you need to provide a cleanup function in useEffect something like this:

return () => { 
   //do something or cancel a subscription
}
Peter Csala
  • 17,736
  • 16
  • 35
  • 75
Sorin
  • 111
  • 1
  • 6
  • 1
    `useEffect` hook callbacks can't be `async`, that is why `getUserInfo` is declared separately and invoked *in* the hook callback. This isn't much of an answer as you've basically said to do the same thing the React warning already stated to do. – Drew Reese Sep 21 '21 at 06:21