React类组件转函数组件后fetch结果useState赋值异常
问题根因
两个核心问题导致state读取异常:
- 闭包捕获旧值:函数组件每次render都会生成新的作用域,
fetch的then回调里启动定时器时,引用的fetchForResults是当前render周期的旧函数,它闭包内捕获的submission还是初始值[],根本拿不到后续更新的state。类组件不存在这个问题是因为this是可变引用,读取this.state时永远能拿到最新值。 setInterval调用写法错误:原代码写的setInterval(fetchForResults(data.id), 1000)会立刻执行一次fetchForResults,再把它的返回值(undefined)作为定时器回调传入,定时器后续执行时根本不会跑你的查询逻辑。类组件里写的setInterval(this.fetchForResults, 1000)是传的函数引用,写法本身是正确的。
另外原代码还有个逻辑错误:接口返回后还执行setSubmitting(true),提交完成应该把提交中的状态设为false。
修复方案
最简修复方式是直接使用then回调里拿到的接口返回data,不需要依赖state读取提交id,同时修正定时器的调用写法:
const [submitting, setSubmitting] = useState(false) const [watingForResult, setWaitingForResult] = useState(false) const [submission, setSubmission] = useState([]); const [scoringResults, setScoringResults] = useState([]); // 补全原代码缺失的state声明 const [result, setResult] = useState([]); const [error, setError] = useState(''); function submitSubmission() { setSubmitting(true); setResult([]); setError(''); const reqData = { code: btoa(code), language: { name: language.name }, users: { username: parseLocalJwt().username }, problem: { name: textToLowerCaseNoSpaces(problem.name) } } fetch(URL + '/submission', { method: 'POST', body: JSON.stringify(reqData), headers: new Headers({ ...getAuthorization(), 'Content-Type': 'application/json' }) }) .then(res => res.json()) .then(resData => { // 修正状态逻辑:拿到返回后关闭提交中状态 setSubmitting(false); setWaitingForResult(true); setSubmission(resData); window.secondsWaiting = new Date().getTime(); // 修正定时器写法:传入箭头函数作为回调,不要直接执行目标函数 window.resultsListener = setInterval(() => { // 直接用当前回调里拿到的最新resData,不需要读闭包里的旧state fetchForResults(resData.id) }, 1000); }); } // 方法接收传入的提交id,不依赖闭包读取state function fetchForResults(submissionId) { console.log('当前查询的提交id:' + submissionId) // 后续查询逻辑直接使用传入的submissionId即可 } // 组件卸载时清除定时器,避免内存泄漏 useEffect(() => { return () => { if (window.resultsListener) clearInterval(window.resultsListener) } }, []) // 提交按钮 <button type="button" onClick={submitSubmission}> Submit! </button>
如果业务中确实需要在任意位置读取最新的submission值,可以搭配useRef存储最新值副本,绕开闭包限制:
const [submission, setSubmission] = useState([]); const submissionRef = useRef([]); // 更新state时同步更新ref const updateSubmission = (val) => { setSubmission(val); submissionRef.current = val; } // 任意位置需要读最新submission时,读取submissionRef.current即可
内容的提问来源于stack exchange,提问作者zemmsoares
相关产品推荐
相关产品推荐

