Apollo Client错误捕获与处理:React组件卸载时报错问题
Hey there! Let's tackle this Apollo error issue you're facing when unmounting components. I've run into this exact problem before, so here are some solid fixes that should stop those unwanted errors from popping up:
The root cause here is often that your Apollo query is still running when the component unmounts, and when it finally resolves (with an error or not), it tries to update state on an unmounted component. Using AbortController lets you cancel the network request entirely before that happens.
Here's how to implement it:
import { useQuery } from '@apollo/client'; import { useEffect, useRef } from 'react'; function YourProblematicComponent() { // Create an AbortController instance tied to this component const abortControllerRef = useRef(new AbortController()); const { data, error } = useQuery(YOUR_GQL_QUERY, { context: { fetchOptions: { // Pass the abort signal to Apollo's fetch call signal: abortControllerRef.current.signal, }, }, }); // Cancel the request when the component unmounts useEffect(() => { return () => abortControllerRef.current.abort(); }, []); // Your existing error handling (only runs if component is mounted) useEffect(() => { if (error) { alert('出现问题,请稍后重试'); } }, [error]); // Rest of your component logic... }
If canceling the request isn't feasible (e.g., you need the query to complete for caching), you can use a ref to track whether the component is still mounted, and only process errors when it is.
import { useQuery } from '@apollo/client'; import { useEffect, useRef } from 'react'; function YourComponent() { const isMounted = useRef(true); const { data, error } = useQuery(YOUR_GQL_QUERY); // Update mounted state when component unmounts useEffect(() => { return () => { isMounted.current = false; }; }, []); // Only handle errors if the component is still mounted useEffect(() => { if (isMounted.current && error) { alert('出现问题,请稍后重试'); } }, [error]); // Rest of your component logic... }
If you want to standardize error handling across your app, you can set up a global error link in Apollo Client, but you'll still need to pair it with component-mounted checks to avoid unmounted component errors.
First, set up the error link:
import { ApolloClient, InMemoryCache, from } from '@apollo/client'; import { onError } from '@apollo/client/link/error'; import { HttpLink } from '@apollo/client/link/http'; const errorLink = onError(({ networkError, graphQLErrors }) => { if (networkError || graphQLErrors) { // Emit a global event instead of directly updating UI window.dispatchEvent( new CustomEvent('apolloGlobalError', { detail: '出现问题,请稍后重试' }) ); } }); const client = new ApolloClient({ link: from([errorLink, new HttpLink({ uri: '/your-gql-endpoint' })]), cache: new InMemoryCache(), });
Then listen for the event in your component, only acting on it if the component is mounted:
useEffect(() => { const handleGlobalError = (e) => { if (isMounted.current) { alert(e.detail); } }; window.addEventListener('apolloGlobalError', handleGlobalError); return () => { window.removeEventListener('apolloGlobalError', handleGlobalError); }; }, []);
My top recommendation is the first method (AbortController) because it stops the problem at the source by canceling the request before it can try to update an unmounted component. The second method is a reliable fallback if you need the query to complete.
内容的提问来源于stack exchange,提问作者Thom Hos

