React StrictMode下Apollo Client错误致history.goBack()执行两次的解决办法
解决React StrictMode下Apollo Client错误触发两次history.goBack的问题
问题出在React StrictMode的开发环境特性:它会重复调用组件渲染和副作用逻辑,导致你在render回调里的history.goBack()执行了两次。以下是几种无需移除StrictMode的解决方案:
方案1:用useRef标记已处理的错误
通过ref变量记录是否已经处理过目标错误,避免重复执行跳转逻辑:
import { useRef } from 'react'; import { useHistory } from 'react-router-dom'; import { useSnackbar } from 'your-snackbar-package'; const Detail = () => { const { snackbar } = useSnackbar(); const history = useHistory(); // 标记是否已处理过该错误 const errorHandled = useRef(false); return ( <Compo query={graphQLQuery}> {({ data, error }) => { const hasSpecificError = error?.graphQLErrors[0]?.extensions?.debugMessage.includes('Specific Error'); if (hasSpecificError) { // 仅在未处理过的情况下执行跳转和提示 if (!errorHandled.current) { errorHandled.current = true; history.goBack(); snackbar('Specific Error'); } return <></>; } else { // 重置标记,确保后续错误能被正常处理 errorHandled.current = false; // 渲染业务组件 return <YourBusinessComponent data={data} />; } }} </Compo> ); };
方案2:用useEffect封装副作用逻辑
把跳转和提示逻辑放到useEffect中,利用依赖项控制触发时机,结合ref避免重复执行:
import { useRef, useEffect } from 'react'; import { useHistory } from 'react-router-dom'; import { useSnackbar } from 'your-snackbar-package'; const Detail = () => { const { snackbar } = useSnackbar(); const history = useHistory(); const errorHandled = useRef(false); return ( <Compo query={graphQLQuery}> {({ data, error }) => { useEffect(() => { const hasSpecificError = error?.graphQLErrors[0]?.extensions?.debugMessage.includes('Specific Error'); if (hasSpecificError && !errorHandled.current) { errorHandled.current = true; history.goBack(); snackbar('Specific Error'); } // 当error变化且不再是目标错误时,重置标记 return () => { if (!hasSpecificError) { errorHandled.current = false; } }; }, [error, history, snackbar]); if (error?.graphQLErrors[0]?.extensions?.debugMessage.includes('Specific Error')) { return <></>; } else { return <YourBusinessComponent data={data} />; } }} </Compo> ); };
方案3:全局错误链接处理(推荐)
将特定错误的处理逻辑移到Apollo Client的全局错误链接中,彻底避开组件render阶段的重复执行问题:
// 创建全局错误链接 import { ApolloLink } from '@apollo/client'; import { useHistory } from 'react-router-dom'; import { useSnackbar } from 'your-snackbar-package'; // 若在非组件环境中使用,可通过react-router的history实例或context获取history const createErrorLink = (history, snackbar) => { return new ApolloLink((operation, forward) => { return forward(operation).map(response => { const hasSpecificError = response.errors?.some( err => err.extensions?.debugMessage.includes('Specific Error') ); if (hasSpecificError) { history.goBack(); snackbar('Specific Error'); } return response; }); }); }; // 在Apollo Client配置中引入该链接 const client = new ApolloClient({ link: ApolloLink.from([ createErrorLink(historyInstance, snackbarInstance), // 你的HTTP链接或其他链接 httpLink ]), // 其他配置项 });
原理说明
- React StrictMode在开发环境中会重复执行render回调和副作用,目的是帮助开发者发现潜在的副作用问题。
- 方案1和2通过ref标记状态,确保同一错误只会触发一次跳转;方案3将逻辑移到Apollo的链路层,不在组件render阶段执行,从根源避免重复触发。
内容的提问来源于stack exchange,提问作者pratibha negi
相关产品推荐
相关产品推荐

