react-routerreact-router-domantdreact-apollo

How do I get Ant Design's message working when I'm navigating away from my page with react-router's useNavigate?


I have an app that does the usual CRUD features, with a list page that will link to creating and editing a resource. Here's a visual:

List
|__Create
|__Edit

The create and edit forms share a very similar form, so i'm just reusing that.

My issue comes from when I create/edit something (this is working), I want to show ant design's message, and navigate back to my list page using useNavigate from react-router. The problem is that when my create/edit is successful, in my success callback, when navigating, I'm not able to see the message pop up at all. However, the message pops up when I'm not navigating away.

My assumption is because maybe my message's contextHolder is on the create/edit page, navigating away will make me lose that, thus not being able to see it.

My async requests are using apollo graphql, so it looks like this

// CreateEditForm.tsx

const navigate = useNavigate();
const [messageApi, contextHolder] = message.useMessage();

// ....
onComplete: (data) => {
  if (!data.error) {
    return messageApi.error('Error');
  }

  messageApi.success('Success');
  return navigate('/list')
re
}

//.... in my CreateEditForm.tsx's render

return (
  <>
    {contextHolder}
    <Form>
    {/* the rest of my code */}
);

What I've tried

Since my assumption is that navigating away from the page is not making the message show, I tried extracting out the contextHolder to a level above in my list, but that didn't seem to work.

EDIT: I realized if I add async/await to messageApi.success, the message will appear for however long the default timeout is, then it will navigate. However, I was hoping for a more synchronous solution.

Minimal reproduction of this issue:

https://codesandbox.io/s/sharp-tree-mysc4n?file=/src/AnotherPage.tsx

Has anyone faced a similar issue like this? Thank you in advance


Solution

  • As per documentation, the recommanded way to way to use message is to wrap your complete app with Application wrapper (App) and use message like this:

    const { message } = App.useApp();.

    Using App, you do not need to handle the contextHolder manually. Antd will take care of it.

    Here's the complete code.

    index.tsx

    import React from "react";
    import ReactDOM from "react-dom/client";
    import App from "./App";
    import { App as AntdApp } from "antd";
    import { createBrowserRouter } from "react-router-dom";
    import { RouterProvider } from "react-router";
    import { AnotherPage } from "./AnotherPage";
    
    const rootElement = document.getElementById("root")!;
    const root = ReactDOM.createRoot(rootElement);
    
    const router = createBrowserRouter([
      { path: "/", element: <App /> },
      { path: "/create", element: <AnotherPage /> }
    ]);
    
    root.render(
      <React.StrictMode>
        <AntdApp>
          <RouterProvider router={router} />
        </AntdApp>
      </React.StrictMode>
    );
    

    AnotherPage.tsx

    import { useNavigate } from "react-router-dom";
    import { Button, App } from "antd";
    
    export const AnotherPage = () => {
      const { message } = App.useApp();
      const navigate = useNavigate();
    
      return (
        <>
          <h3>Create Page</h3>
          <Button
            onClick={() => {
              message.success("Thing is created!");
              navigate("/");
            }}
          >
            Click to show msg and navigate back
          </Button>
        </>
      );
    };