向useEffect传入双函数触发卸载错误的技术咨询
问题解答
1. 是否允许在一个useEffect中调用两个函数或使用两个独立的useEffect?
完全允许两种写法,但更推荐根据逻辑拆分到独立的useEffect:
- 如果两个函数的触发条件(依赖项)完全一致,可以合并在同一个useEffect里。
- 如果两个函数的触发逻辑不同(比如获取投票数据是在组件挂载/投票ID变化时,而投票是在选择答案后),拆分到独立的useEffect会让代码更清晰,避免不必要的重复执行。
你的场景里,获取投票详情和提交投票的触发条件本就不同:前者应该在pollIview变化时执行,后者在answer变化时执行,分开写更合理。
2. 如何解决组件卸载后状态更新的错误?
你遇到的问题主要是isMounted作用域错误、fetch语法错误、依赖项配置不当导致的,以下是具体修复方案:
核心问题拆解
- 你之前在
voting函数内部重新定义了isMounted,外层useEffect的清理函数修改的是外层变量,根本影响不到voting内部的标记,导致异步操作完成后依然会执行状态更新。 - fetch请求语法错误:
JSON.stringify(...).then()是错的,应该是对fetch返回的Promise调用.then()。 - 函数组件里误用了
this.setState,得用React的useState钩子声明状态。 - useEffect依赖项错配:获取投票数据的useEffect不该依赖
answer,不然每次选答案都会重复请求投票详情,完全没必要。
修正后的代码示例
第一步:先声明必要的状态
import { useState, useEffect } from 'react'; function PollVoter({ pollIview }) { const [poll, setPoll] = useState(null); const [answer, setAnswer] = useState(''); const [error, setError] = useState(false); // 用useState声明error状态
第二步:拆分useEffect,分别处理获取投票和提交投票
// 获取投票详情:组件挂载或pollIview变化时执行 useEffect(() => { let isMounted = true; const fetchPoll = async () => { try { const resp = await fetch(`/api/polls/${pollIview}`); const result = await resp.json(); if (isMounted) { if (result.noSuchPoll) { setPoll(false); } else { setPoll(result.pollInfo); } } } catch (err) { console.log('error in pollvoter.js ', err); if (isMounted) { setError(true); } } }; fetchPoll(); return () => { isMounted = false; }; }, [pollIview]); // 只依赖pollIview,只有ID变化时才重新请求 // 提交投票:answer变化时执行(加判断避免无效请求) useEffect(() => { // 没选答案或poll还没加载完成,直接返回 if (!answer || !poll?.id) return; let isMounted = true; const submitVote = async () => { try { await fetch('/api/vote', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ poll_id: poll.id, answer: answer, }), }); if (isMounted) { // 推荐用React路由跳转代替location.reload,避免页面刷新 // navigate(`/polls/${poll.id}`); window.location.reload(); } } catch (err) { console.log('error submitting vote: ', err); if (isMounted) { setError(true); } } }; submitVote(); return () => { isMounted = false; }; }, [answer, poll?.id]); // 依赖answer和poll.id,确保poll加载完成后才提交
额外优化建议
- 尽量别用
window.location.reload(),用React Router的navigate方法跳转,用户体验更好。 - 可以用
AbortController给fetch请求加取消令牌,比isMounted标记更可靠,能真正取消未完成的请求:
// 获取投票详情的useEffect优化(用AbortController) useEffect(() => { const controller = new AbortController(); const signal = controller.signal; const fetchPoll = async () => { try { const resp = await fetch(`/api/polls/${pollIview}`, { signal }); const result = await resp.json(); setPoll(result.noSuchPoll ? false : result.pollInfo); } catch (err) { // 忽略取消请求的错误 if (err.name !== 'AbortError') { console.log('error in pollvoter.js ', err); setError(true); } } }; fetchPoll(); return () => { controller.abort(); // 组件卸载时直接取消请求 }; }, [pollIview]);
内容的提问来源于stack exchange,提问作者Ana Ptskialadze
相关产品推荐
相关产品推荐

