Search code examples
javascriptreactjstypescriptantd

Why getPopupContainer doesn't work correctly in my react app?


I'm getting this warning:

Warning: [antd: Modal] Static function can not consume context like dynamic theme. Please use 'App' component instead.

Then, the Modal.confirm() doesn't show correctly because it renders outside of the dependency tree and doesn't inherit the styles.

I'm using react 18.2.0 and antd 5.4.6.

Here I leave the code

main.tsx

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
  <Suspense fallback={<Loader />}>
     <ThemeWrapper>
         <Outlet />
      </ThemeCoWrapper>
  </Suspense>
);
const ThemeWrapper: React.FC<ThemeProps> = ({ children }) => {
  const [theme, setTheme] = useState<any>(defaultTheme);

  const modalContainerRef = useRef<HTMLDivElement>(null);

  return (
    <ConfigProvider
      theme={theme}
      getPopupContainer={() => modalContainerRef.current as HTMLElement}
    >
      <div ref={modalContainerRef}>{children}</div>
    </ConfigProvider>
  );
};

export default ThemeWrapper;

Example of function in which I use the Modal.confirm() inside the wrapper

const onLogout = () => {
    Modal.confirm({
      title: (
        <b>
          {intl.formatMessage({
            id: 'labelCloseSession',
          })}
        </b>
      ),
      content: intl.formatMessage({
        id: 'labelLogoutConfirm',
      }),
      centered: true,
      onOk: async () => {
        try {
          await loginService.logout();
          removeUserSession();
          globalContext.dispatch({
            type: GlobalStateActionType.LOGOUT,
          });
          navigate('/login');
        } catch (error) {}
      },
    });
  };

I tried to use document.getElemetById instead of useRef but it didn't work.


Solution

  • Because static function in React 18 concurrent mode will not well support. In v5, Antd recommends to use hooks for the static replacement (read here). So, you will need to set up your project like what they stated here: https://ant.design/components/app#basic-usage

    In your case, try:

    • For the ThemeWrapper component
    • import { App } from 'antd';
    • And then, wrap the <div> element in <App></App> like:
      <ConfigProvider
           theme={theme}
           getPopupContainer={() => modalContainerRef.current as HTMLElement}
      >
           <App>
               <div ref={modalContainerRef}>{children}</div>
           </App>
      </ConfigProvider>
      

    Here, I provide the codesandbox example: https://codesandbox.io/s/text-and-link-component-antd-5-0-2-forked-9svf8v?file=/demo.tsx They recommend encapsulating App at the top level in the application

    After that, you will need to use modal.confirm({}) from App.useApp(); instead of the old Modal.confirm() in your onLogout function.