繁体   English   中英

如何在 react.js 中克隆/复制事件

[英]How to clone/copy events in react.js

我创建了一个生成模态对话框的组件。 您可能知道,模态必须作为子元素放置在根(主体)元素内,以化解任何父元素 styles。

为了完成上述过程,我使用 vanilla js 克隆我的 Modal 组件并将其 append 复制到 body 中,如下所示:

  useEffect(() => {
    const modalInstance = document.getElementById('modal-instance-' + id);

    if (modalInstance) {
      const modal = modalInstance.cloneNode(true);
      modal.id = 'modal-' + id;

      const backdrop = document.createElement('div');
      backdrop.id = 'modal-backdrop';
      backdrop.className = 'hidden fixed top-0 bottom-0 start-0 end-0 bg-black bg-opacity-75 z-[59]';
      backdrop.addEventListener('click', toggleModal);

      document.body.appendChild(backdrop);
      document.body.appendChild(modal);

      const closeBtn = document.querySelector(`#modal-${id} > [data-close='modal']`);
      closeBtn.addEventListener('click', toggleModal);
    }

到目前为止一切顺利,Modal 完美运行; 但是当我将带有事件的元素作为子元素传递给我的 Modal 组件时,问题就开始出现了。

<Modal id='someId' size='lg' show={showModal} setShow={setShowModal} title='some title'>
  <ModalBody>
    Hellowwww...
    <Button onClick={() => alert('working')} type='button'>test</Button>
  </ModalBody>
</Modal>

上面的按钮有一个onClick事件,当我将整个模式和 append 克隆到正文时必须克隆该事件。

长话短说

有没有其他方法可以在没有 vanilla js 的情况下完成相同的机制? 如果没有,我该如何解决问题?

您应该使用来自ReactDom https://beta.reactjs.org/apis/react-dom/createPortalcreatePortal API

function Modal (props) {
   const wrapperRef = useRef<HTMLDivElement>(null);

   useIsomorphicEffect(() => {
      wrapperRef.current = document.getElementById(/* id of element */)
   }, []) 
  
   return createPortal(<div>/* Modal content */ </div>, wrapperRef )

}

useIsomorphic 效果挂钩是export const useIsomorphicEffect = typeof document?== 'undefined': useLayoutEffect; useEffect; export const useIsomorphicEffect = typeof document?== 'undefined': useLayoutEffect; useEffect;

因为“警告:useLayoutEffect 在服务器上不执行任何操作,因为它的效果无法编码为服务器渲染器的 output 格式。”

在花了一些时间寻找解决这个问题的方法后,我发现我经历的整个过程都是错误的,显然有更强大的方法来完成这个。

因此,这是我的最终工作组件,以防您需要在项目中学习或使用它:

import {useEffect, useState} from 'react';
import Button from '@/components/Button';
import {X} from 'react-bootstrap-icons';
import {createPortal} from 'react-dom';

export const Modal = ({id, title, className = '', size = 'md', show = false, setShow, children}) => {
  const [domReady, setDomReady] = useState(false);

  const sizeClass = {
    sm: 'top-28 bottom-28 start-2 end-2 sm:start-28 sm:end-28 sm:start-60 sm:end-60 xl:top-[7rem] xl:bottom-[7rem] xl:right-[20rem] xl:left-[20rem]',
    md: 'top-16 bottom-16 start-2 end-2 xl:top-[5rem] xl:bottom-[5rem] xl:right-[10rem] xl:left-[10rem]',
    lg: 'top-2 bottom-2 start-2 end-2 sm:top-3 sm:bottom-3 sm:start-3 sm:end-3 md:top-4 md:bottom-4 md:start-4 md:end-4 lg:top-5 lg:bottom-5 lg:start-5 lg:end-5',
  };

  useEffect(() => {
    setDomReady(true);
  }, []);

  return (
    domReady ?
      createPortal(
        <>
          <div className={`${show ? '' : 'hidden '}fixed top-0 bottom-0 start-0 end-0 bg-black bg-opacity-75 z-[59]`} onClick={() => setShow(false)}/>
          <div id={id}
               className={`${show ? '' : 'hidden '}fixed ${sizeClass[size]} bg-white dark:bg-gray-800 text-gray-600 dark:text-gray-200 drop-shadow-lg rounded-lg z-[60] ${className}`}>
            <Button
              className='absolute top-3 end-3'
              type='button'
              size='sm'
              color='secondaryOutlined'
              onClick={() => setShow(false)}
            ><X className='text-xl'/></Button>

            {title && <div className='absolute top-4 start-3 end-16 font-bold'>{title}</div>}
            <div>{children}</div>
          </div>
        </>
        , document.getElementById('modal-container'))
      : null
  );
};

export const ModalBody = ({className = '', children}) => {
  return (
    <div className={`mt-10 p-3 ${className}`}>
      <div className='border-t border-gray-200 dark:border-gray-600 pt-3'>
        {children}
      </div>
    </div>
  );
};

用法:

_app.js

<Html>
  <Head/>
  <body className='antialiased' dir='rtl'>
  <Main/>
  <div id='modal-container'/> <!-- Pay attention to this --!>
  <NextScript/>
  </body>
</Html>

任何你需要模态的地方:

<Modal id='someId' size='lg' show={showModal} setShow={setShowModal} title='Some title'>
  <ModalBody>
    Hellowwww...
    <Button onClick={() => alert('working')} type='button'>Test</Button>
  </ModalBody>
</Modal>

我应该提到我使用tailwindcss库来设计我的模态和react-bootstrap-icons图标。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM