Search code examples
reactjsgoogle-cloud-firestorefirebase-authenticationreact-hooksuse-effect

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


I am having an issue when I am trying to fetch some data. For some reason, I keep receiving this error:

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.

This is the entire component, where I am fetching the data and then passing it in to the drawerconfig component This data then gets passed down further and further in to other components:

export default function Root() {
  const [userImage, setUserImage] = useState();
  const [userName, setUserName] = useState();
  const [name, setName] = useState();
  const [urlName, setUrlName] = useState();
  const [userID, setUserID] = useState();
  const [followerCount, setFollowerCount] = useState();
  const [followingCount, setFollowingCount] = useState();

  const [links, setLinks] = useState([{link: null}]);

  const [pageLoading, setPageLoading] = useState(true);

  // Following counts, displayname, image
  const fetchData = useCallback((data) => {
    const dataRef = firestore().collection('usernames');
    const usersLinks = firestore().collection('links');

    // Fetch user Links
    usersLinks.doc(data.data().urlName).onSnapshot((doc) => {
      const entries =
        doc.data() === undefined ? [undefined] : Object.values(doc.data());
      entries[0] === undefined ? setLinks([{link: null}]) : setLinks(entries);
    });

    dataRef.doc(data.data().urlName).onSnapshot((snap) => {
      // Fetch users image
      setUserImage(snap.data().imageUrl);
      setUserID(snap.data().displayName);
      setUserName(snap.data().userName);
      setUrlName(data.data().urlName);
      setName(snap.data().displayName);
      setFollowerCount(snap.data().followers);
      setFollowingCount(snap.data().following);
      setPageLoading(false);
    });
  }, []);

  // Fetch all data here
  useEffect(() => {
    auth().onAuthStateChanged((user) => {
      if (user !== null) {
        if (user.emailVerified) {
          const cleanup = firestore()
            .collection('users')
            .doc(user.uid)
            .onSnapshot(fetchData);

          return cleanup;
        }
      }
    });
  }, [fetchData]);

  return (
    <>
      {/* ALL SCREENS */}
      {pageLoading ? (
        <ActivityIndicator size="large" color="black" />
      ) : (
        <DrawerConfig
          links={links}
          username={userName}
          userimage={userImage}
          userid={userID}
          displayname={name}
          urlname={urlName}
          followerCount={followerCount}
          followingCount={followingCount}
        />
      )}
    </>
  );
}

Any help would be appreciated, Thank you


Solution

  • Looks like you need to modify your useEffect a bit - I don't think your listener is being unsubscribed when you unmount this component.

      // Fetch all data here
      useEffect(() => {
        return auth().onAuthStateChanged((user) => {
        ...
        })
      })
    

    .onAuthStateChanged() returns the unsubscribe function; useEffect accepts an unsubscribe function as a return to be executed on unmount.