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
相关产品推荐
相关产品推荐

