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

React+TypeScript中useEffect卸载清理状态与设置状态冲突问题

问题

在React+TypeScript项目中,点击客户端条目会设置对应的clientIdForRegister和clientNameForRegister,用于后续API请求;同时需要在用户离开页面时清理这两个状态,但之前的实现出现冲突:

  • 点击客户端条目设置状态的代码:
const setClient = () => {
  setClientIdForRegister(id)
  setClientNameForRegister(nome)
}

return (
  <ClientSearchResultInfos onClick={setClient}>
    <div className="client_and_email">
      <h2>{nome}</h2>
      <span>{email}</span>
    </div>
    <CaretRight size={30} />
  </ClientSearchResultInfos>
)
  • API请求使用状态的代码:
const handleRegisterReceipt = async (data: FormDataProps) => {
  try {
    const dataToSend = {
      cliente: {
        id: clientIdForRegister,
      },
      dataPagamento: data.dataPagamento,
      valor: data.valor,
    }

    const response = await api.post('/pagamentos', dataToSend)
  }
}
  • 点击客户端后同步表单输入框的代码:
useEffect(() => {
  const getClientName = async () => {
    if (clientNameForRegister) {
      setValue('cliente', clientNameForRegister)
    }
  }

  getClientName()
}, [setValue, clientNameForRegister])

尝试过两种清理状态的写法都有问题:

  1. 父组件无依赖的useEffect返回清理函数,导致每次重渲染都执行清理,clientIdForRegister始终为null:
// 父组件: 
const cleanUp = () => {
  setClientNameForRegister(null)
  setClientIdForRegister(null)
}

useEffect(() => {
  return cleanUp
})
  1. 使用isMountedRef标记挂载状态,但卸载时无法清理状态:
// 父组件:
const isMountedRef = useRef<boolean>(true)

useEffect(() => {
  return () => {
    isMountedRef.current = false

    if (isMountedRef.current) {
      setClientNameForRegister(null)
      setClientIdForRegister(null)
    }
  }
})

需要实现:用户离开页面时清理状态,同时正常发送API请求时clientIdForRegister有效。

解决方案

方案1:给父组件的清理useEffect添加空依赖数组

这是最简单直接的方法,空依赖数组的useEffect仅在组件挂载时执行一次,返回的清理函数只会在组件卸载时触发,不会在每次重渲染时执行,完美解决状态被频繁重置的问题:

// 父组件
useEffect(() => {
  return () => {
    setClientNameForRegister(null);
    setClientIdForRegister(null);
  };
}, []); // 空依赖数组,仅在组件卸载时执行清理逻辑

方案2:结合路由监听精准控制清理时机

如果是通过路由切换离开页面,可以监听路由变化,仅在离开当前页面路由时清理状态(适用于react-router v6):

import { useLocation } from 'react-router-dom';

// 父组件
const location = useLocation();
// 假设当前页面的路由路径为 '/register-receipt'
const currentPagePath = '/register-receipt';

useEffect(() => {
  return () => {
    // 当路由切换到其他页面时,清理状态
    if (location.pathname !== currentPagePath) {
      setClientNameForRegister(null);
      setClientIdForRegister(null);
    }
  };
}, [location]);

方案3:修正isMountedRef的使用方式

之前的写法错误在于清理函数中先将isMountedRef.current设为false,再判断,导致清理逻辑永远不执行。正确的做法是:

  • 用isMountedRef标记组件是否处于挂载状态
  • 在设置状态前检查标记,避免卸载后触发状态更新
  • 清理函数直接执行状态重置(组件卸载后设置状态不会触发无效重渲染,无需额外判断)
// 父组件
const isMountedRef = useRef<boolean>(true);

// 标记组件卸载状态
useEffect(() => {
  return () => {
    isMountedRef.current = false;
  };
}, []);

// 修改设置状态的函数,仅在组件挂载时更新状态
const setClient = () => {
  if (isMountedRef.current) {
    setClientIdForRegister(id);
    setClientNameForRegister(nome);
  }
};

// 组件卸载时清理状态
useEffect(() => {
  return () => {
    setClientNameForRegister(null);
    setClientIdForRegister(null);
  };
}, []);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 23:12:47