skip to Main Content

I have a React component like this

export const NewComponent: React.FC<NewComponentProps> = ({prop1, prop2, prop3 }) => {
    const getAbsPositionDialog = () => {
        const element = document.getElementById('modalDialogId');
        if (!element) {
            return { x: 0, y: 0 };
        }
        const dialogPosition = element.getBoundingClientRect();
        return { x: dialogPosition.x, y: dialogPosition.y };
    }

    const dialogPosition = getAbsPositionDialog();
    const horizontal = dialogPosition.x + 100;
    const vertical = dialogPosition.y + 100;

    const toasterId = useId("toaster");
    const { dispatchToast } = useToastController(toasterId);
    const notify = (title: string) => {
        dispatchToast(
            <Toast>
              <ToastTitle>{title}</ToastTitle>
            </Toast>,
            { intent: "success" }
          );
    }

    React.useEffect(() => {
        <Toaster
            toasterId={toasterId}
            position="top-start"
            offset={{horizontal: horizontal, vertical: vertical}}
            pauseOnHover
            pauseOnWindowBlur
            timeout={1000}
        />
    }, [horizontal, vertical]);

    return (
        <div>
            <Button onClick={notify("Copied!")}/>
        </div>
    );
};

The idea is to show a Toast component when clicking on the button. This NewComponent is shown within a modal dialog and the location of the Toast notification is set at the top-start relative to the whole window screen size, so it works well like that, showing the Toast notification at that position. No problem with it.

The problem comes when I try to add some offset based on the modalDialogId. I want to know where the dialog is located so I can move the Toast notification closer to it, but when the component is executed, the modal dialog is not loaded yet and therefore the offset always returns as 0, 0.

I tried to use the React.useEffect to render the Toaster which is where I set my offset later, but that didn’t seem to work.

Is there a way to achieve this?

2

Answers


  1. Yes, you can achieve this by using the useRef hook. The useRef hook creates a mutable ref that you can use to store a value. The value of the ref can be changed at any time, and the changes will be reflected in the rendered output.

    Here is the code with the useRef hook:

    const dialogRef = useRef(null);
    
    const getAbsPositionDialog = () => {
      if (!dialogRef.current) {
        return { x: 0, y: 0 };
      }
      const dialogPosition = dialogRef.current.getBoundingClientRect();
      return { x: dialogPosition.x, y: dialogPosition.y };
    }
    
    const dialogPosition = getAbsPositionDialog();
    const horizontal = dialogPosition.x + 100;
    const vertical = dialogPosition.y + 100;
    
    ...
    
    React.useEffect(() => {
      dialogRef.current = document.getElementById('modalDialogId');
    }, []);
    
    ...

    In this code, the dialogRef ref is created and initialized to null. The getAbsPositionDialog function is then called to get the absolute position of the modal dialog. The horizontal and vertical variables are then set to the x and y coordinates of the modal dialog, respectively.

    The useEffect hook is then used to set the dialogRef ref to the document.getElementById(‘modalDialogId’) element. This is done so that the dialogRef ref is always pointing to the modal dialog element.

    The useEffect hook also takes an empty array as its second argument. This means that the useEffect hook will only run once, when the component is first rendered.

    I hope this helps! Let me know if you have any other questions.

    Login or Signup to reply.
  2. you can set toaster postion style to absolute :

    containerStyle= {{ position : "absolute"}}
    

    and set the modal container position to relative

    position: "relative",
    

    demo :

    import toast, { Toaster } from "react-hot-toast";
    import Modal from "react-modal";
    
    const dismissToast = () => toast.remove();
    
    const successToast = () => toast.success("Here is your toast.");
    
    export default function App() {
      const customStyles = {
        content: {
          top: "20%",
          left: "20%",
          right: "20%",
          bottom: "20%",
          background: "#e5e5e5"
        }
      };
    
      return (
        <div>
          <Modal
            style={customStyles}
            isOpen={true}
            contentLabel="Minimal Modal Example"
          >
            <div style={{ position: "relative", margin: "auto", height: "100%" }}>
              <button onClick={successToast}>Success toast</button>
              <button onClick={dismissToast}>Remove all toasts</button>
              <hr />
              <h1>i am modal</h1>
              <Toaster
                position="bottom-left"
                containerClassName="fff"
                containerStyle={{ position: "absolute", inset: 0 }}
                toastOptions={{
                  duration: 3000000,
                  iconTheme: {
                    primary: "red",
                    secondary: "white"
                  },
                  role: "status",
                  ariaLive: "polite",
                  style: {
                    background: "green",
                    color: "whitesmoke"
                  }
                }}
              />
            </div>
          </Modal>
        </div>
      );
    }
    

    and the toast will be shown inside the modal without any extra code

    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search