如何在 React 中创建可重用的自定义模态组件?

elc*_*ias 1 ajax modal-dialog reactjs

我对 React 中的模态概念有疑问。当使用带有 jQ​​uery 的服务器端渲染模板时,我习惯于有一个始终可用的空全局模态模板(包含在始终扩展的基本模板中)。然后在进行 AJAX 调用时,我只是填充了模态……像这样:

$('.modal-global-content').html(content);
$('.modal-global').show();
Run Code Online (Sandbox Code Playgroud)

那么我如何在 React 中实现这个概念呢?

小智 5

There are a few ways of doing this. The first involves passing in the modal state from a parent component. Here's how to do this - first with the parent App.js component:

// App.js

import React from "react";

import Modal from "./Modal";

const App = () => {
  const [showModal, updateShowModal] = React.useState(false);

  const toggleModal = () => updateShowModal(state => !state);

  return (
    <div>
      <h1>Not a modal</h1>
      <button onClick={toggleModal}>Show Modal</button>
      <Modal canShow={showModal} updateModalState={toggleModal} />
    </div>
  );
}

export default App;
Run Code Online (Sandbox Code Playgroud)

And here's the Modal.js child component that will render the modal:

// Modal.js

import React from "react";

const modalStyles = {
  position: "fixed",
  top: 0,
  left: 0,
  width: "100vw",
  height: "100vh",
  background: "blue"
};

const Modal = ({ canShow, updateModalState }) => {
  if (canShow) {
    return (
      <div style={modalStyles}>
        <h1>I'm a Modal!</h1>
        <button onClick={updateModalState}>Hide Me</button>
      </div>
    );
  }

  return null;
};

export default Modal;
Run Code Online (Sandbox Code Playgroud)

This way is perfectly fine, but it can get a bit repetitive if you're reusing the modal in many places throughout your app. So instead, I would recommend using the context API.

Define a context object for your modal state, create a provider near the top of your application, then whenever you have a child component that needs to render the modal, you can render a consumer of the modal context. This way you can easily nest your modal deeper in your component tree without having to pass callbacks all the way down. Here's how to do this - first by creating a context.js file:

// context.js

import React from "react";

export const ModalContext = React.createContext();
Run Code Online (Sandbox Code Playgroud)

Now the updated App.js file:

// App.js

import React from "react";

import { ModalContext } from "./context";
import Modal from "./Modal";

const App = () => {
  const [showModal, updateShowModal] = React.useState(false);

  const toggleModal = () => updateShowModal(state => !state);

  return (
    <ModalContext.Provider value={{ showModal, toggleModal }}>
      <div>
        <h1>Not a modal</h1>
        <button onClick={toggleModal}>Show Modal</button>
        <Modal canShow={showModal} updateModalState={toggleModal} />
      </div>
    </ModalContext.Provider>
  );
}

export default App;
Run Code Online (Sandbox Code Playgroud)

And lastly the updated Modal.js file:

// Modal.js

import React from "react";

import { ModalContext } from "./context";

const modalStyles = {
  position: "fixed",
  top: 0,
  left: 0,
  width: "100vw",
  height: "100vh",
  background: "blue"
};

const Modal = () => {
  return (
    <ModalContext.Consumer>
      {context => {
        if (context.showModal) {
          return (
            <div style={modalStyles}>
              <h1>I'm a Modal!</h1>
              <button onClick={context.toggleModal}>Hide Me</button>
            </div>
          );
        }

        return null;
      }}
    </ModalContext.Consumer>
  );
};

export default Modal;
Run Code Online (Sandbox Code Playgroud)

Here's a Codesandbox link with a working version using context. I hope this helps!


小智 2

解决此问题的一种方法是使用 css 和 JSX。

这就是应用程序,我可以有任何东西,例如按钮,链接,任何东西让我们假设我们有一个链接(react-router-dom),可以将我们重定向到DeletePage

删除页面呈现Modal 您将提供模态框的title和为actionsprops

const App = () => {
  return(
    <Link to="/something/someid">SomeAction</Link>
  )
}

const DeletePage = () => {
  return(
    <Modal
      title="Are you sure you want to delete this"
      dismiss={() => history.replace("/")}
      action={() => console.log("deleted") }
      />
  )
}
Run Code Online (Sandbox Code Playgroud)
莫代尔

const Modal = (props) => {
  return(
      <div>
        <div className="background" onClick={props.dismiss}/>
        <h1>{props.title}</h1>
        <button onClick={props.dismiss}>Cancel</button>
        <button onClick={props.action}>Delete</button>
      </div>
    )
}
Run Code Online (Sandbox Code Playgroud)
  • 将模态的设定z-index为较高的数字
  • position: fixed模态成分的
  • 当用户单击背景时,模型将消失(实现该模型的方法有很多,例如模态状态、重定向等,我将重定向作为其中一种方法)
  • cancel button也有相同的 onClick 函数,即关闭
  • Delete buttonaction函数是否通过 props 传递

由于CSS,这个方法有一个缺陷,因为如果你的父组件有一个position属性,relative那么这个方法就会被破坏。
无论 z-index 有多高,模态框都将保留在父级内部


React-Portal 拯救了我们


React Portal 以自己的方式创建一个“门户”
您可能拥有的 React 代码将在内部呈现DOMid 的#root(在大多数情况下)

因此,为了将我们呈现Modal为最顶层,我们 在公共文件 中创建另一个
DOM element例如<div id="modal"></div>index.html

ReactModal组件代码会稍微改变


const Modal = (props) => {
  return ReactDOM.createPortal(
      <div>
        <div className="background" onClick={props.dismiss}/>
        <h1>{props.title}</h1>
        <button onClick={props.dismiss}>Cancel</button>
        <button onClick={props.action}>Delete</button>
      </div>
    ),document.querySelector("#modal")
}
Run Code Online (Sandbox Code Playgroud)

休息都是一样的