How to create reusable custom modal component in React?

送分小仙女□ 提交于 2021-02-16 15:20:48

问题


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?


回答1:


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!




回答2:


One way you can solve this problem by using css and JSX.

this is the app and i can have anything like a button a link anything Lets assume we have a link (react-router-dom) which redirects us to a DeletePage

The Delete Page renders a Modal You will provide the title and the actions of the Modal as 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") }
      />
  )
}

Modal

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>
    )
}
  • set the z-index of the modal a high number
  • position: fixed of the modal component
  • when the user will click on the background the model will go away ( many ways to implement that like with modal state, redirect, etc i have taken the redirect as one of the ways )
  • cancel button also has the same onClick function which is to dismiss
  • Delete button has the action function passed through props

this method has a flaw because of css because if your parent component has a position property of relative then this will break.
The modal will remain inside the parent no matter how high the z-index is


To Save us here comes React-Portal


React portal creates a 'portal' in its own way
The react code you might have will render inside DOM with id of #root ( in most cases )

So to render our Modal as the top most layer we create another
DOM element eg <div id="modal"></div> in the public index.html file

The Modal react component code will slightly change


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

rest is all the same



来源:https://stackoverflow.com/questions/62502954/how-to-create-reusable-custom-modal-component-in-react

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!