React 18严格模式下Axios重复请求的解决方法
解决React 18严格模式下Axios请求重复发起的问题
在React 18严格模式中,组件会经历挂载→卸载→重新挂载的过程,这会导致componentDidMount执行两次。你原本用state中的dataFetched做判断,但第二次挂载时state会重置为初始值,所以这个判断无法阻止重复请求。以下是几种可靠的解决方案:
方案1:使用类组件实例变量标记请求状态
实例变量不会随组件卸载重置,能有效避免重复发起请求:
修改你的代码,在constructor中添加一个实例变量标记请求是否已发起,然后在componentDidMount中判断这个变量:
import React, { Component } from "react"; import axios from "axios"; import history from "../../history"; class Authenticate extends Component { constructor(props) { super(props); this.state = { token: "", dataFetched: false, }; // 添加实例变量标记请求状态 this.isRequestInitiated = false; this.getCallBack = this.getCallBack.bind(this); } async componentDidMount() { // 判断实例变量,而非state if (!this.isRequestInitiated) { this.isRequestInitiated = true; // 标记请求已发起 try { const res = await axios({ method: "get", url: "/users/twitter_login", }); if (res.data.boolean) { this.setState({ dataFetched: true, token: res.data.token }); this.getCallBack(res.data.url); } else { history.push({ pathname: "/login", }); } } catch (error) { console.log(error); // 请求失败时可以重置标记,允许重试 this.isRequestInitiated = false; } } } getCallBack(url) { history.push(url); } render() { return ( <div> <img src="http://placekitten.com/g/200/300"></img> </div> ); } } export default Authenticate;
方案2:使用Axios取消令牌(CancelToken)
如果需要更精细的控制(比如取消重复的未完成请求),可以用Axios的取消机制:
import React, { Component } from "react"; import axios from "axios"; import history from "../../history"; class Authenticate extends Component { constructor(props) { super(props); this.state = { token: "", dataFetched: false, }; this.cancelTokenSource = null; this.getCallBack = this.getCallBack.bind(this); } componentDidMount() { if (!this.cancelTokenSource) { this.cancelTokenSource = axios.CancelToken.source(); axios({ method: "get", url: "/users/twitter_login", cancelToken: this.cancelTokenSource.token }).then((res) => { if (res.data.boolean) { this.setState({ dataFetched: true, token: res.data.token }); this.getCallBack(res.data.url); } else { history.push({ pathname: "/login", }); } }).catch((error) => { if (axios.isCancel(error)) { console.log("请求已取消", error.message); } else { console.log(error); this.cancelTokenSource = null; // 失败后重置,允许重试 } }); } } componentWillUnmount() { // 组件卸载时取消未完成的请求 if (this.cancelTokenSource) { this.cancelTokenSource.cancel("组件卸载,取消请求"); } } getCallBack(url) { history.push(url); } render() { return ( <div> <img src="http://placekitten.com/g/200/300"></img> </div> ); } } export default Authenticate;
补充:迁移到函数组件的方案
如果后续考虑升级到函数组件,可以用useRef保存请求状态,配合useEffect实现单次请求:
import React, { useEffect, useRef, useState } from "react"; import axios from "axios"; import history from "../../history"; const Authenticate = () => { const [token, setToken] = useState(""); const [dataFetched, setDataFetched] = useState(false); const isRequestInitiated = useRef(false); useEffect(() => { if (!isRequestInitiated.current) { isRequestInitiated.current = true; const fetchData = async () => { try { const res = await axios({ method: "get", url: "/users/twitter_login", }); if (res.data.boolean) { setDataFetched(true); setToken(res.data.token); history.push(res.data.url); } else { history.push("/login"); } } catch (error) { console.log(error); isRequestInitiated.current = false; } }; fetchData(); } }, []); return ( <div> <img src="http://placekitten.com/g/200/300"></img> </div> ); }; export default Authenticate;
内容的提问来源于stack exchange,提问作者arthur
相关产品推荐
相关产品推荐

