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

l0oc07j2  于 2023-02-08  发布在  React
关注(0)|答案(3)|浏览(138)

我对React中模态的概念有一个问题。当使用jQuery服务器端呈现的模板时,我习惯于有一个空的全局模态模板始终可用(包括在始终扩展的基础模板中)。然后当进行 AJAX 调用时,我只是填充了模态..类似于以下内容:

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

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

pkwftd7m

pkwftd7m1#

有几种方法可以做到这一点。第一种方法涉及从父组件传入模态状态。下面是如何做到这一点-首先是父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的链接,其中包含一个使用上下文的工作版本。我希望这能有所帮助!

3npbholx

3npbholx2#

解决这个问题的一种方法是使用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函数,该函数用于
  • Delete button具有通过属性传递的action函数

这个方法有一个css的缺陷,因为如果你的父组件有一个relative的position属性,那么它将被破坏。
无论z索引有多高,模态都将保留在父对象内部

保存我们的是React之门

React门户以自己的方式创建“门户
您可能拥有的react代码将在id为#rootDOM内部呈现(大多数情况下)
为了将Modal渲染为最顶层,我们创建了另一个
DOM element例如公共index.html文件中的<div id="modal"></div>
Modal react组件代码将略有更改

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

休息都一样

soat7uwm

soat7uwm3#

使用React-Portal和模态发生器

我一直在努力寻找一种好的、标准的方式来处理react中的模态。有些人建议使用本地状态模态,有些人建议使用模态上下文提供程序和一个函数来呈现模态窗口,或者使用像ChakraUI这样的预置ui库,它提供了自己的Modal组件。但是使用这些可能有点棘手,因为它们往往会使web ui中相对简单的概念过于复杂。
经过一番研究,我已经习惯了门户的方式,因为这似乎是最明显的方式。所以这个想法是,创建一个可重用的模态组件,将子组件作为 prop ,并使用本地setState有条件地呈现每个模态。这样,与页面或组件相关的每个模态都只出现在相应的组件中。

奖金:

要创建使用相同设计的相似模态,可以使用一个jsx生成器函数,该函数将很少的颜色和其他属性作为其参数。
工作代码:

// Generate modals for different types
// All use the same design
// IMPORTANT: Tailwind cannot deduce partial class names sent as arguments, and
// removes them from final bundle, safe to use inline styling
const _generateModal = (
  initialTitle: string,
  image: string,
  buttonColor: string,
  bgColor: string = "white",
  textColor: string = "rgb(55 65 81)",
  buttonText: string = "Continue"
) => {
  return ({ title = initialTitle, text, isOpen, onClose }: Props) => {
    if (!isOpen) return null;
    return ReactDom.createPortal(
      <div className="fixed inset-0 bg-black bg-opacity-80">
        <div className="flex h-full flex-col items-center justify-center">
          <div
            className="relative flex h-1/2 w-1/2 flex-col items-center justify-evenly rounded-xl lg:w-1/4"
            style={{ color: textColor, backgroundColor: bgColor }}
          >
            <RxCross2
              className="absolute top-0 right-0 mr-5 mt-5 cursor-pointer text-2xl"
              onClick={() => onClose()}
            />
            <h1 className="text-center text-3xl font-thin">{title}</h1>
            <h3 className="text-center text-xl font-light tracking-wider opacity-80">
              {text}
            </h3>
            <img
              src={image}
              alt="modal image"
              className="hidden w-1/6 lg:block lg:w-1/4"
            />
            <button
              onClick={() => onClose()}
              className="rounded-full px-16 py-2 text-xl text-white"
              style={{ backgroundColor: buttonColor }}
            >
              {buttonText}
            </button>
          </div>
        </div>
      </div>,
      document.getElementById("modal-root") as HTMLElement
    );
  };
};

export const SuccessModal = _generateModal(
  "Success!",
  checkimg,
  "rgb(21 128 61)" // green-700
);
export const InfoModal = _generateModal(
  "Hey there!",
  infoimg,
  "rgb(59 130 246)" // blue-500
);
export const ErrorModal = _generateModal(
  "Face-plant!",
  errorimg,
  "rgb(190 18 60)", // rose-700
  "rgb(225 29 72)", // rose-600
  "rgb(229 231 235)", // gray-200
  "Try Again"
);

相关问题