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

如何解决React中已卸载组件执行setState报错的相关问题

为什么点击搜索按钮会执行清理代码

useEffect的清理函数不止在组件卸载时执行,只要useEffect的依赖项发生变化,React都会先执行上一次渲染生成的effect返回的清理函数,再执行本次渲染对应的新effect。你这里的useEffect依赖了search状态,点击搜索按钮会调用setSearch修改search的值,自然会触发上一次effect的清理逻辑。
你最开始用普通let mounted = true的写法能跑通,本质是每次组件重渲染时都会重新创建这个mounted变量并赋值为true,相当于每次新的effect用的都是新的变量,旧的清理函数修改的是上一次渲染的mounted变量,互不干扰。但这种写法不符合React的状态持久化规则,所以ESLint会报错。你用useRef的时候出错是写法不对,不是useRef本身的问题。


远端搜索的正确实现方式

推荐优先用AbortController取消过期请求,比单纯判断组件是否挂载更合理,既能避免state更新警告,还能减少不必要的网络请求,示例代码如下:

function ListStock() {
    const [data, setData] = useState([]);
    const [search, setSearch] = useState();

    useEffect(() => {
        // 每次effect创建新的AbortController实例
        const controller = new AbortController();
        const signal = controller.signal;

        async function fetchData() {
            try {
                const {start_date, end_date} = search;
                // 请求的时候传入signal参数,fetch/axios等主流请求库都支持
                const result = await getDataStock(start_date, end_date, signal);
                if (!signal.aborted) { // 只处理未被取消的请求结果
                    setData(result.data);
                }
            } catch (err) {
                // 请求被取消的错误属于预期行为,不用处理
                if (err.name !== 'AbortError') {
                    console.error('请求失败', err);
                }
            }
        }

        if (search) { // 避免search为undefined时报解构错误
            fetchData();
        }

        // 依赖变化/组件卸载时取消当前请求
        return () => {
            controller.abort();
        }

    }, [search])

    const handleSearch = () => {
        setSearch({
            start_date: moment().subtract(1, 'month').format('YYYY-MM-DD'),
            end_date: moment().format('YYYY-MM-DD')
        });
    }

    return (
        <div>
            <input type="text" id="keyword">
            <input type="button" onClick={handleSearch} value="Search">
            {data}
        </div>
    )
}

如果你坚持要用mounted标记的方式,正确的useRef写法如下:

function ListStock() {
    const isMountedRef = useRef(true);
    const [data, setData] = useState([]);
    const [search, setSearch] = useState();

    // 空依赖的useEffect只在组件挂载/卸载时执行,专门维护挂载状态
    useEffect(() => {
        isMountedRef.current = true;
        return () => {
            isMountedRef.current = false;
        }
    }, []);

    useEffect(() => {
        async function fetchData() {
            const {start_date, end_date} = search;
            const result = await getDataStock(start_date, end_date);
            
            if (result && isMountedRef.current) {
                setData(result.data);
            }
        }
        if (search) {
            fetchData();
        }
    }, [search])
    // 其余逻辑保持不变
}

额外可以给搜索按钮加点击防抖,避免用户短时间多次点击发起重复请求。


能不能忽略这类ESLint警告

绝对不建议。这类警告是ESLint基于React的运行规则检测出的潜在逻辑问题,忽略之后表面上不会直接报错,但会留下内存泄漏、旧请求结果覆盖新请求、状态更新异常等隐藏bug,后续排查成本极高。


内容的提问来源于stack exchange,提问作者Bar .W

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 02:09:01