You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React useEffect清理函数正确用法及组件卸载后状态更新警告问题排查

Fixing "Can't perform a React state update on an unmounted component" Warning in Your React Login Component

Let's break down the issues in your code and fix that warning step by step.

First Issue: Misused useEffect Cleanup Function

Looking at your useEffect code, you’ve got the logic backwards—you placed the checkUserType function inside the cleanup callback (the return statement of useEffect), which only runs when the component unmounts or before the effect re-runs. That’s not what you intended: you want to check if the user is already logged in when the component mounts or when dependencies like userData change.

Your current (incorrect) code:

useEffect( () => {
  const checkUserType = () => {
    // Check is user logged in and redirects to dashboard
    if (userData !== null && isAuth) {
      // ... redirect logic
    }
  }
  return () => checkUserType() // Runs on cleanup, NOT on mount/update
}, [agency, history, userData, userUID, isAuth])

Fix this first: Move the checkUserType() call directly inside the useEffect callback so it runs when the component mounts or when dependencies update. You don’t need a cleanup function for this specific logic:

useEffect( () => {
  const checkUserType = () => {
    if (userData !== null && isAuth) {
      if (userData.type === 'employee') {
        history.push(`/${agency}/dashboard/${userUID}`)
      } else if (userData.type === 'crewagency') {
        history.push(`/${agency}/crew-dashboard/`)
      } else if ( userData.type === 'keyagency') {
        history.push(`/${agency}/key-dashboard/`)
      }
    }
  }
  checkUserType() // Run on mount or when dependencies change
}, [agency, history, userData, userUID, isAuth])

Second Issue: Async Operations Updating State After Component Unmounts

The main cause of the warning is your submit function: when the user triggers login, you start async calls (Firebase auth + Firestore document fetch). If the component unmounts (e.g., the user navigates away) before these operations finish, the subsequent setLoading, setAgencyErr, or setError calls will try to update state on an unmounted component.

To fix this, we’ll track if the component is still mounted before updating state:

  1. Add a ref to track mounted state (import useRef first):
import React, {useEffect, useState, useRef} from 'react'

// Inside your Login component:
const isMounted = useRef(true);
  1. Update the ref in a useEffect cleanup function to mark the component as unmounted:
useEffect(() => {
  return () => {
    isMounted.current = false; // Set to false when component unmounts
  };
}, []); // Empty dependency array runs once on mount, cleanup on unmount
  1. Modify all state updates in your async submit function to check if the component is still mounted first:
const submit = () => {
  setAgencyErr(false)
  setLoading(true)
  firebase.auth()
    .signInWithEmailAndPassword(email, password)
    .then(res => {
      let uid = res.user.uid;
      db.collection('users').doc(uid).get()
        .then( doc => {
          if (!isMounted.current) return; // Exit if component is unmounted

          const resData = doc.data();
          if (resData.data.agencyId === agency) {
            // Redirect logic remains unchanged
            if (resData.data.type === 'employee') {
              history.push(`/${agency}/dashboard/${uid}`)
            } else if (resData.data.type === 'crewagency') {
              history.push(`/${agency}/crew-dashboard`)
            } else if ( resData.data.type === 'keyagency') {
              history.push(`/${agency}/key-dashboard`)
            }
          } else {
            firebase.auth().signOut()
            setAgencyErr(true)
          }
          setLoading(false)
        })
        .catch(err => {
          if (!isMounted.current) return;
          console.log('User info error', err)
          setLoading(false)
        })
      setIsAuth(true)
    })
    .catch(err => {
      if (!isMounted.current) return;
      console.log('Login error: ', err.message)
      setError(true)
      setLoading(false)
    })
}

Bonus: Simplify Unnecessary Code

You can remove the redundant JSON.stringify/JSON.parse calls—Firebase returns usable objects directly:

// Instead of this:
let resData = JSON.stringify(res.user)
resData = JSON.parse(resData)
let uid = resData.uid

// Just do this:
let uid = res.user.uid;

// And for Firestore:
const resData = doc.data(); // No need to stringify/parse

Final Notes

  • The useEffect cleanup function is designed to cancel subscriptions, clear timers, or clean up resources that could cause memory leaks when the component unmounts. Your initial use of it for login checks was the opposite of its intended purpose.
  • By tracking the mounted state with a ref, we ensure we never update state on an unmounted component, eliminating the warning and preventing potential memory leaks.

内容的提问来源于stack exchange,提问作者Romanas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 04:47:28