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

React函数组件内部函数如何获取更新后的state值?

问题修复方案

核心问题分析

你的问题是闭包陷阱导致的:dataFetch函数在初始渲染时被创建,捕获了当时的sportTypeId(初始值1),但后续sportTypeId更新后,被定时器引用的旧版dataFetch依然持有旧的state值,所以打印结果不符合预期。另外用useState存储定时器ID,会因为异步更新特性导致无法正确清理旧定时器。


具体修复步骤

1. 用useRef存储定时器ID

useState的更新是异步的,无法保证获取到最新的定时器ID,改用useRef保存,确保能正确清理旧定时器:

// 替换原来的const [timer, setTimer] = useState(null)
const timerRef = useRef(null);

2. 用useCallback包裹dataFetch,添加依赖

让dataFetch在依赖的state更新时重新创建,确保每次能获取到最新的state值:

const dataFetch = useCallback(() => {
    console.log('sportTypeId==>', sportTypeId); // 现在能拿到最新值
    const obj = {
        sportTypeId: sportTypeId ?? 1, // 简化空值判断写法
        betradarCategoryId,
        leagueName,
        matchState,
        startIndex,
        orderByLeague
    };
    dispatch(getMatches(obj));
    
    // 清理旧定时器
    if (timerRef.current) {
        clearTimeout(timerRef.current);
    }
    // 设置新定时器并保存到ref
    timerRef.current = setTimeout(dataFetch, 5000);
}, [sportTypeId, betradarCategoryId, leagueName, matchState, startIndex, orderByLeague, dispatch]);

3. 调整useEffect逻辑,避免重复调用

移除冗余的isMounted状态,用useEffectOnce做初始调用,同时监听sportTypeId变化,触发重新请求:

// 初始加载调用一次,组件卸载时清理定时器
useEffectOnce(() => {
    dataFetch();
    return () => {
        if (timerRef.current) {
            clearTimeout(timerRef.current);
        }
    };
});

// 当sportTypeId变化时,立即触发新请求并重置定时器
useEffect(() => {
    if (timerRef.current) {
        clearTimeout(timerRef.current);
    }
    dataFetch();
}, [sportTypeId, dataFetch]);

完整修复后的代码

import React, { useState, useEffect, useRef, useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useEffectOnce } from 'usehooks-ts';
import { SportsTypeNavbar } from '../../../mobile/components'
import { getMatches } from '../../../store/actions/mobileSportsActions';
import './mHome.css'

function MHome(props) {
    const dispatch = useDispatch()
    const timerRef = useRef(null); // 改用useRef存储定时器ID

    const [sportTypeId, setSportTypeId] = useState(1);
    const [betradarCategoryId, setBetradarCategoryId] = useState(0);
    const [leagueName, setLeagueName] = useState();
    const [matchState, setMatchState] = useState('home');
    const [startIndex, setStartIndex] = useState(0);
    const [orderByLeague, setOrderByLeague] = useState(false);

    const get_Matches = useSelector(state => state.mobileSportsReducers.getMatches)
    const SportTypeList = useSelector(state => state.mobileSportsReducers.getTypeList);

    const dataFetch = useCallback(() => {
        console.log('sportTypeId==>', sportTypeId); // 现在能获取最新值
        const obj = {
            sportTypeId: sportTypeId ?? 1,
            betradarCategoryId,
            leagueName,
            matchState,
            startIndex,
            orderByLeague
        };
        dispatch(getMatches(obj));
        
        // 清理旧定时器
        if (timerRef.current) {
            clearTimeout(timerRef.current);
        }
        // 设置新定时器
        timerRef.current = setTimeout(dataFetch, 5000);
    }, [sportTypeId, betradarCategoryId, leagueName, matchState, startIndex, orderByLeague, dispatch]);

    // 初始加载调用
    useEffectOnce(() => {
        dataFetch();
        // 卸载时清理定时器
        return () => {
            if (timerRef.current) {
                clearTimeout(timerRef.current);
            }
        };
    });

    // sportTypeId变化时重新请求
    useEffect(() => {
        if (timerRef.current) {
            clearTimeout(timerRef.current);
        }
        dataFetch();
    }, [sportTypeId, dataFetch]);

    const sportActiveFunc = (index) => {
        setSportTypeId(index);
    };

    console.log('sportTypeId==>', sportTypeId); // 正常输出最新值
    return (
        <SportsTypeNavbar sportActiveFunc={sportActiveFunc} />
    );
};
export default MHome;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 20:25:26