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

2022-01-17 00:00:00 reactjs modal-dialog ajax

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

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

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

解决方案

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

//App.js从反应"导入反应;从./Modal"导入模态;const App = () =>;{const [showModal, updateShowModal] = React.useState(false);const toggleModal = () =>updateShowModal(state => !state);返回 (

<h1>不是模态</h1><button onClick={toggleModal}>显示模式</button><Modal canShow={showModal} updateModalState={toggleModal}/></div>);}导出默认应用程序;

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

//Modal.js从反应"导入反应;常量 modalStyles = {位置:固定",顶部:0,左:0,宽度:100vw",高度:100vh",背景:蓝色"};const Modal = ({ canShow, updateModalState }) =>{如果(可以显示){返回 (<div 样式={modalStyles}><h1>我是模态的!</h1><button onClick={updateModalState}>隐藏我</button></div>);}返回空值;};导出默认模态;

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

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

//context.js从反应"导入反应;导出 const ModalContext = React.createContext();

现在更新了 App.js 文件:

//App.js从反应"导入反应;从./context"导入{ ModalContext };从./Modal"导入模态;const App = () =>;{const [showModal, updateShowModal] = React.useState(false);const toggleModal = () =>updateShowModal(state => !state);返回 (<ModalContext.Provider 值={{ showModal, toggleModal }}>

<h1>不是模态</h1><button onClick={toggleModal}>显示模式</button><Modal canShow={showModal} updateModalState={toggleModal}/></div></ModalContext.Provider>);}导出默认应用程序;

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

//Modal.js从反应"导入反应;从./context"导入{ ModalContext };常量 modalStyles = {位置:固定",顶部:0,左:0,宽度:100vw",高度:100vh",背景:蓝色"};const Modal = () =>{返回 (<ModalContext.Consumer>{上下文=>{如果(context.showModal){返回 (<div 样式={modalStyles}><h1>我是模态的!</h1><button onClick={context.toggleModal}>隐藏我</button></div>);}返回空值;}}</ModalContext.Consumer>);};导出默认模态;

这是一个 Codesandbox 链接使用上下文的版本.我希望这会有所帮助!

I have a problem with the concept of modals in React. When using server side rendered templates with jQuery I was used to have one empty global modal template always available (included in base template that was always extended). Then when making AJAX call I just populated modal..something like this:

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

So how do I make this concept in React?

解决方案

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;

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;

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();

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;

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;

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

相关文章