How to create reusable custom modal component in React?

后端 未结 2 1593
北荒
北荒 2021-01-24 00:41

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 (

相关标签:
2条回答
  • 2021-01-24 01:09

    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!

    0 讨论(0)
  • 2021-01-24 01:29

    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

    0 讨论(0)
提交回复
热议问题