nyphur
nyphur

Reputation: 2896

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

Upvotes: 3

Views: 4539

Answers (2)

Muhammad Nouman Rafique
Muhammad Nouman Rafique

Reputation: 1576

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>
    </>
  );
};

Upvotes: 5

Drew Reese
Drew Reese

Reputation: 203323

This Message API is a bit interesting/odd IMO, but I was able to get things working by moving the message usage into a layout route that exists above the main routes, and provides the messageApi via a React context.

Example:

Index.tsx

import React from "react";
import ReactDOM from "react-dom/client";
import { createBrowserRouter, RouterProvider, Outlet } from "react-router-dom";
import { message } from "antd";
import App from "./App";
import { AnotherPage } from "./AnotherPage";

const rootElement = document.getElementById("root")!;
const root = ReactDOM.createRoot(rootElement);

Create an AppLayout component to use the useMessage hook and provide the contextHolder to the UI and the messageApi to the Outlet context.

const AppLayout = () => {
  const [messageApi, contextHolder] = message.useMessage();

  return (
    <>
      {contextHolder}
      <Outlet context={{ messageApi }} />
    </>
  );
};

Render AppLayout as a layout route.

const router = createBrowserRouter([
  {
    element: <AppLayout />,
    children: [
      {
        path: "/",
        element: <App />
      },
      {
        path: "/create",
        element: <AnotherPage />
      }
    ]
  }
]);

root.render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
);

AnotherPage

Access the messageApi that is provided via the Outlet's context provider.

import { useNavigate, useOutletContext } from "react-router-dom";
import { Button } from "antd";

export const AnotherPage = () => {
  const { messageApi } = useOutletContext();
  const navigate = useNavigate();

  return (
    <>
      <h3>Create Page</h3>
      <Button
        onClick={() => {
          messageApi.success("Thing is created!");
          navigate(-1);
        }}
      >
        Click to show msg and navigate back
      </Button>
    </>
  );
};

Edit how-do-i-get-ant-designs-message-working-when-im-navigating-away-from-my-page

Upvotes: 0

Related Questions