React StrictMode导致数组数据重复翻倍问题咨询
问题描述
我知道React StrictMode会导致日志重复输出,但从没遇到过它改变数组或变量值的情况。关闭StrictMode时,data状态正常包含25条数据;开启后,data变成50条,是前25条的重复。
自定义Hook代码
const useRequest = (page) => { const [loading, setLoading] = useState(false) const [data, setData] = useState([]) const [error, setError] = useState('') useEffect(() => { const url = `someURL/{page}/25` <-- 这里是要获取的条目数 setLoading(true) setError('') fetch(url) .then(res => res.json()) .then(data => setData(prev => [...prev, ...data.items]) }) setLoading(false) },[page]) return { loading, data, error} }
App.js代码
const App = () => { const [pageNumber,setPageNumber] = useState(1) const { loading, data, error } = useRequest(pageNumber) console.log(data) return ( <div className="App"> <main> {data.map((user,id) => <Card key={id} userData={user} /> )} </main> </div> ); }
问题原因
React StrictMode在开发环境下会重复执行副作用函数(比如useEffect),目的是帮你检测潜在的代码问题。你的代码里存在两个关键问题:
- 没有给异步请求添加清理逻辑,导致StrictMode触发的两次useEffect都发起了page=1的请求,两次请求完成后都执行了
setData(prev => [...prev, ...data.items]),第一次把数组变成25条,第二次又追加25条相同数据,最终得到50条重复内容; setLoading(false)放在fetch外部同步执行,不管请求是否完成都会立刻把loading设为false,属于逻辑错误。
修复方案
修改自定义Hook,添加请求取消逻辑并修正loading状态的更新时机:
const useRequest = (page) => { const [loading, setLoading] = useState(false); const [data, setData] = useState([]); const [error, setError] = useState(''); useEffect(() => { // 创建AbortController用于取消请求 const abortController = new AbortController(); const signal = abortController.signal; const fetchData = async () => { setLoading(true); setError(''); try { // 修正模板字符串的变量插入错误 const url = `someURL/${page}/25`; const res = await fetch(url, { signal }); const responseData = await res.json(); setData(prev => [...prev, ...responseData.items]); } catch (err) { // 忽略取消请求的错误 if (err.name !== 'AbortError') { setError(err.message); } } finally { // 请求完成或失败后再更新loading状态 setLoading(false); } }; fetchData(); // 清理函数:组件卸载或page变化时取消未完成的请求 return () => abortController.abort(); }, [page]); return { loading, data, error }; };
额外说明
- 模板字符串里的变量需要用
${变量名}包裹,你之前写的{page}是错误的,会导致URL不正确; - 使用
AbortController后,当StrictMode第二次执行useEffect时,会先触发第一次useEffect的清理函数,取消第一次的请求,这样只有第二次的请求会完成,避免重复追加数据; - 如果你的场景是切换分页(而非加载更多),可以把
setData(prev => [...prev, ...responseData.items])改成setData(responseData.items),每次切换page直接替换数据,不会有重复问题。
内容的提问来源于stack exchange,提问作者James Hetfield
相关产品推荐
相关产品推荐

