DEV Community

Rupesh Ghosh
Rupesh Ghosh

Posted on

How to Implement a Global Error Handling Popup in React Native with React Query

Handling errors gracefully in a React Native application is essential for providing a smooth user experience. One effective approach is to implement a global error handling popup that informs users of issues and allows them to retry failed actions. In this blog post, we will walk through how to achieve this using React Native and React Query.

Setting Up Your Project

Ensure you have a React Native project set up. If not, you can create one using Expo.

Creating the PopupController

PopupController will manage the visibility of the GlobalPopup. This controller can be used by other components to show or hide the popup.

export interface PopupRef {
  show: (failedQuery: QueryKey) => void;
  hide: () => void;
}

export default class PopupController {
  static popupRef: MutableRefObject<PopupRef>;

  static setGlobalPopupRef = (ref: MutableRefObject<PopupRef>) => {
    this.popupRef = ref;
  };

  static showGlobalPopup = (failedQuery: QueryKey) => {
    this.popupRef.current?.show(failedQuery);
  };

  static hideGlobalPopup = () => {
    this.popupRef.current?.hide();
  };
}
Enter fullscreen mode Exit fullscreen mode

Creating the GlobalPopup Component

The GlobalPopup component will display error messages and provide an option to retry failed actions. It uses useLayoutEffect to set the ref of GlobalPopup in the popupController. It also uses useImperativeHandle to customise the handle exposed as a ref, allowing it to show and hide the popup.

const GlobalPopup = () => {
  const [visible, setVisible] = useState(false);
  const [failedQueries, setFailedQueries] = useState<QueryKey[]>([]);
  const queryClient = useQueryClient();
  const popupRef = useRef<PopupRef>();

  // Using useLayoutEffect to set the ref of GlobalPopup in popupController
  useLayoutEffect(() => {
    PopupController.setGlobalPopupRef(popupRef as MutableRefObject<PopupRef>);
  }, []);

  // Using useImperativeHandle to customize the handle exposed as a ref
  useImperativeHandle(
    popupRef,
    () => ({
      show: (failedQuery: QueryKey) => {
        setVisible(true);
        if (!failedQueries.includes(failedQuery)) {
          setFailedQueries([...failedQueries, failedQuery]);
        }
      },
      hide: () => {
        setVisible(false);
        setFailedQueries([]);
      },
    }),
    [failedQueries]
  );

  const handleTryAgain = () => {
    failedQueries.forEach(async (query) => {
      await queryClient.invalidateQueries({ queryKey: query });
    });
    PopupController.hideGlobalPopup();
  };

  return (
    <Modal visible={visible}>
      <View>
        <Text>This is a global error popup</Text>
        <Button onPress={handleTryAgain} title="Try Again" />
      </View>
    </Modal>
  );
};
Enter fullscreen mode Exit fullscreen mode

Integrating GlobalPopup in the App Component

Now, integrate the GlobalPopup in the top-level component of your application and set up the React Query client to handle errors globally.

const queryClient = new QueryClient({
  queryCache: new QueryCache({
    // Overwriting queryClient to handle errors globally
    onError: (_, query) => {
      PopupController.showGlobalPopup(query.queryKey);
    },
  }),
});
Enter fullscreen mode Exit fullscreen mode

How it works

  • When any component in the application makes an API call using useQuery and it fails, the configured onError handler in App.tsx is triggered.
  • The onError handler calls PopupController.showGlobalPopup with the failed query key (e.g., ["key"]).
  • The show function in the popup reference object updates the visibility state of GlobalPopup and adds the failed query key to the internal list.
  • The global error popup becomes visible, displaying a generic error message and a "Try Again" button.
  • Clicking "Try Again" triggers handleTryAgain in the popup. This function iterates through the failed queries, invalidates them using queryClient, and calls PopupController.hideGlobalPopup to hide the popup.
  • React Query refetches the invalidated queries, potentially resolving the error.

By incorporating these enhancements, you can create a robust and informative error handling experience for your React Native applications.

Sample project with global error handling implementation: github

Top comments (1)

Collapse
 
blenderman profile image
BBM

This is a great guide! Could you clarify what specific kind of global errors would necessitate such a popup?