skip to Main Content

i’am trying to implement à modal with React, in this modal i want to position the modal relatively to the the element wich trigger it (which is not à parent),
in this set up i can access the the reference to the triggering element like you see here

        console.log(
          "console button triggering modal offset" + event.target.offsetTop
        );

but i can’t access the reference to the modal, this code generate an error

  useEffect(() => {
    console.log("loggin ref width   " + modalRef.current.offsetWidth);
  }, [modalRef]);

i have this erro message

Uncaught TypeError: Cannot read properties of undefined (reading 'offsetWidth')
    at UserParamModal2.js:44:1
    at commitHookEffectListMount (react-dom.development.js:23150:1)
    at commitPassiveMountOnFiber (react-dom.development.js:24926:1)
    at commitPassiveMountEffects_complete (react-dom.development.js:24891:1)
    at commitPassiveMountEffects_begin (react-dom.development.js:24878:1)
    at commitPassiveMountEffects (react-dom.development.js:24866:1)
    at flushPassiveEffectsImpl (react-dom.development.js:27039:1)
    at flushPassiveEffects (react-dom.development.js:26984:1)

when i remove the CSSTransition component every thing is fine,
here is the full code,
i would appreciate any help

import {
  React,
  forwardRef,
  useEffect,
  useImperativeHandle,
  useRef,
  useState,
} from "react";
import ReactDOM from "react-dom";

import "./UserParamModal2.css";
import { CSSTransition } from "react-transition-group";

const UserParamModalOverlay = forwardRef((props, ref) => {
  const content = (
    <div className={`userparammodal ${props.className}`} ref={ref}>
      {props.children}
    </div>
  );
  return ReactDOM.createPortal(
    content,
    document.getElementById("modal-user-param")
  );
});

const UserParamModal2 = forwardRef((props, ref) => {
  const [visible, setVisible] = useState(false);
  const modalRef = useRef();

  useImperativeHandle(ref, () => {
    return {
      toggle(event) {
        console.log(
          "console button triggering modal offset" + event.target.offsetTop
        );
        setVisible((prev) => {
          return !prev;
        });
      },
    };
  });

  useEffect(() => {
    console.log("loggin ref width   " + modalRef.current.offsetWidth);
  }, [modalRef]);

  return (
    <CSSTransition
      in={visible}
      mountOnEnter
      unmountOnExit
      timeout={200}
      classNames="userparammodal"
    >
      <UserParamModalOverlay {...props} ref={modalRef}></UserParamModalOverlay>
    </CSSTransition>
  );
});

export default UserParamModal2;

2

Answers


  1. You are getting the error because modalRef.current is undefined when useEffect runs for the first time. Just add null check or if(modalRef.current) condition to avoid this error.

    You may also need to update the dependency if useEffect doesn’t run.

    useEffect(() => {
        console.log("loggin ref width   " + modalRef.current?.offsetWidth);
      }, [modalRef.current]);
    
    Login or Signup to reply.
  2. To assign that trigger modal to the element, you can use the useRef hook to get a reference to the triggering element and then calculate the state of the modal based on that reference Here is a general outline of how you can achieve this.

    Get a reference to the triggering element: After defining the modal triggering element, in the parent component, use the useRef hook to create a ref for the triggering element.

    Calculate the modal state: In the modal component, use the useEffect hook to calculate the state of the modal based on the state of the triggering element. You can use methods like getBoundingClientRect() to get the shape and location of the trigger object.

    Use position styles: Set the position of the modal using inline styles or CSS classes based on calculated space.

    import React, { useRef } from "react";
    import UserParamModal2 from "./UserParamModal2";
    
    const ParentComponent = () => {
      const triggerRef = useRef();
    
      return (
        <div>
          <button ref={triggerRef} onClick={() => UserParamModal2.toggle()}>
            Open Modal
          </button>
          <UserParamModal2 triggerRef={triggerRef} />
        </div>
      );
    };
    
    export default ParentComponent;
    
    
    
    
    import React, { forwardRef, useEffect, useState } from "react";
    
    const UserParamModal2 = forwardRef(({ triggerRef }, ref) => {
      const [visible, setVisible] = useState(false);
    
      useEffect(() => {
        const triggerElement = triggerRef.current;
    
        if (triggerElement) {
          // Calculate the position of the modal relative to the trigger element
          const { top, left, height, width } = triggerElement.getBoundingClientRect();
    
          // Apply position styles
          // You can adjust these values based on your requirements
          const modalStyle = {
            top: `${top + height + 10}px`,
            left: `${left}px`,
          };
    
          // Apply the styles to the modal
          // In this example, we set it as inline styles, but you can use CSS classes too
          // Be sure to apply "position: absolute;" to the modal container
          Object.assign(ref.current.style, modalStyle);
        }
      }, [triggerRef]);
    
      // The rest of your modal code
    
      return (
        <div ref={ref} className={`userparammodal ${visible ? "visible" : ""}`}>
          {/* Modal content */}
        </div>
      );
    });
    
    export default UserParamModal2;
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search