【问题标题】:How to create reusable custom modal component in React?如何在 React 中创建可重用的自定义模态组件?
【发布时间】:2020-06-21 18:41:38
【问题描述】:

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

$('.modal-global-content').html(content);
$('.modal-global').show();

那么我如何在 React 中提出这个概念呢?

【问题讨论】:

  • 你不会在 React 中做类似的事情,这是简短的回答。您想改为有条件地呈现模态组件。要在模式之间共享功能,请创建共享模式组件,或使用现成的组件:github.com/reactjs/react-modal
  • @prekolna 好吧,我想我正在尝试创建一个共享模式,但我很难理解如何在 ajax 调用中填充这个模式(使用另一个组件)。 axios.get('/api/product/' + state.target.id + '/locales' ).then(res => { /* 在这里用另一个组件及其数据填充模态 */ });

标签: reactjs ajax modal-dialog


【解决方案1】:

有几种方法可以做到这一点。第一个涉及从父组件传入模态状态。下面是如何做到这一点 - 首先是父 App.js 组件:

// 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;

这里是Modal.js 子组件,它将呈现模态:

// 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;

这种方式非常好,但是如果您在整个应用程序的许多地方重用模态框,它可能会有点重复。因此,我建议使用上下文 API。

为您的模态状态定义一个上下文对象,在您的应用程序顶部附近创建一个提供程序,然后当您有一个需要呈现模态的子组件时,您可以呈现模态上下文的消费者。这样,您可以轻松地将模态嵌套在组件树的更深处,而无需一直向下传递回调。下面是如何做到这一点 - 首先创建一个context.js 文件:

// context.js

import React from "react";

export const ModalContext = React.createContext();

现在更新App.js文件:

// 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;

最后是更新的Modal.js 文件:

// 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;

这是一个Codesandbox 链接,其中包含使用上下文的工作版本。我希望这会有所帮助!

【讨论】:

  • 感谢使用上下文对我来说绝对是新的。我有一个可能很愚蠢的问题,在名为 Catalog 的组件中,我过滤了产品,在编辑 rpodcut 时,我想在此模式中呈现组件 ProductEdit 如何在 axios.get 方法中调用它?
  • @elcarcharias 我认为最好的方法是将产品的想法保存到上下文中作为currentProductId。然后,在您的Modal 组件中,您可以使用useEffect 发出GET 请求,传入context.currentProductId。 GET 请求将返回产品详细信息。您现在可以将这些详细信息保存在 Modal 组件的本地状态中,然后根据需要进行渲染
【解决方案2】:

您可以通过使用 css 和 JSX 来解决此问题。

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

删除页面呈现Modal 您将模态的titleactions 提供为props

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") }
      />
  )
}

模态

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>
    )
}
  • 将模态的z-index 设置为大数字
  • 模态组件的position: fixed
  • 当用户点击背景时,模型将消失( 有很多方法来实现它,比如模态状态、重定向等 已将重定向作为其中一种方式)
  • cancel button也有同样的onClick函数,就是dismiss
  • Delete button 具有通过 props 传递的 action 函数

这个方法有一个缺陷,因为 css 因为如果你的父组件 具有relative 的位置属性,那么这将中断。
无论 z-index 有多高,模态都将保留在父级中


React-Portal 来拯救我们


React 门户以自己的方式创建一个“门户”
您可能拥有的反应代码将在 DOM 中呈现,id 为 #root(在大多数情况下)

因此,为了将我们的Modal 渲染为最顶层,我们创建了另一个
DOM element 例如&lt;div id="modal"&gt;&lt;/div&gt; 在公共index.html 文件中

Modalreact 组件代码会略有变化


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")
}

其他都一样

【讨论】:

  • 谢谢,但是如果我想在 modal 中显示另一个组件,我可以将它作为其 prop 传递给 Modal 组件吗?
  • @elcarcharias 绝对是return &lt;div&gt;{props.anotherComponent}&lt;/div&gt;
  • 但是如果我想在 ajax 调用中将此组件传递给模态道具?
  • @elcarcharias 在componentDidMount 中进行调用,并根据响应呈现Modal 并将组件作为props 传递
猜你喜欢
  • 1970-01-01
  • 2021-12-25
  • 2020-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-09
  • 2021-07-11
  • 2016-01-26
相关资源
最近更新 更多