React - Toast Render Twice BlueprintJS

22 Views Asked by At

I am trying to implement a react hook that allows me to generate a toast of the Blueprint JS library (version 5.7.2) at any time. The code I wrote is as follows:

import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useState,
} from "react";
import type { OverlayToasterProps, ToastProps } from "@blueprintjs/core";
import { OverlayToaster, Position } from "@blueprintjs/core";
import { createRoot } from "react-dom/client";
import { v4 } from "uuid";

function createToaster(
  props?: OverlayToasterProps,
  container = document.body,
): Promise<NotificationCtx> {
  const containerElement = document.createElement("div");
  container.appendChild(containerElement);
  const root = createRoot(containerElement);
  return new Promise<NotificationCtx>((resolve, reject) => {
    root.render(
      <OverlayToaster
        {...props}
        usePortal={false}
        ref={(toaster) => {
          if (toaster) {
            resolve({
              show: (toast: ToastProps, key?: string) => {
                return toaster.show(toast, key ?? v4());
              },
              dismiss: (key: string) => {
                toaster.dismiss(key);
              },
            });
          } else {
            reject(new Error("Toaster is null"));
          }
        }}
      />,
    );
  });
}

export interface NotificationCtx {
  dismiss: (key: string) => void;
  show: (toast: ToastProps, key?: string) => string;
}

const INITIAL_CONTEXT = {
  show: () => "",
  dismiss: () => null,
};
const NotificationContext = createContext<NotificationCtx>(INITIAL_CONTEXT);

export function NotificationProvider(p: { children: JSX.Element }) {
  const [toaster, setToaster] = useState<NotificationCtx>(INITIAL_CONTEXT);

  useEffect(() => {
    createToaster({
      position: Position.TOP_RIGHT,
    }).then((t) => {
      setToaster(t);
    });
  }, []);

  return (
    <NotificationContext.Provider value={toaster}>
      {p.children}
    </NotificationContext.Provider>
  );
}

export function useNotifications(): NotificationCtx {
  return useContext(NotificationContext);
}

In another component I render to the screen by writing:

const notification = useNotifications();

  notification.show(
    {
      message: "Hello world!",
      intent: "success",
      icon: "tick",
    },
  );

The result is that at the end of rendering the page I see the toast 2 times.

Where am I going wrong?

0

There are 0 best solutions below