Search code examples
javascriptreactjsreact-bootstrap

Problem with React-Bootstrap Modal not appearing with custom Context Menu


I'm new to React and Web-Dev in general. I'm creating a custom context menu when a user right-clicks on the web-app I'm creating. A modal should pop-up when you click on the custom context menu. However, the modal isn't showing up. What am I doing wrong?

Here is the code for the context menu:

class ContextMenu extends React.Component {
    constructor(props){
       super(props);
       this.state = {
          xPos: "0px",
          yPos: "0px",
          showMenu: false,
       }
    }

    componentDidMount() {
         document.addEventListener("click", this.handleClick);
         document.addEventListener("contextmenu", this.handleContextMenu);
    }

    componentWillUnmount() {
        document.removeEventListener("click", this.handleClick);
        document.removeEventListener("contextmenu", this.handleContextMenu);
    }

    handleClick = (e) => {
        if (this.state.showMenu) this.setState({ showMenu: false });
    }

    handleContextMenu = (e) => {
        e.preventDefault();

        this.setState({
           xPos: `${e.pageX}px`,
           yPos: `${e.pageY}px`,
           showMenu: true,
        })
    }

    alertClicked = () => {
          <Modal show = {true}>
             <Modal.Header closeButton>
                 <Modal.Title>Modal Title</Modal.Title>
             </Modal.Header>
         </Modal>
    }

    render() {
        const { showMenu, yPos, xPos } = this.state;
        if (showMenu) {
            return (
                <div
                    className="menu-container"
                       style={{
                       top: yPos,
                       left: xPos,
                       zIndex: 9,
                       position: "absolute",
                 }}>
                    <ListGroup>
                        <ListGroup.Item action onClick = {this.alertClicked}>
                             Show Modal
                        </ListGroup.Item>
                    </ListGroup>
            </div>
        );
    } else return null;
}

}

This is the article I'm following to create the context menu.


Solution

  • You can't return JSX from an event handler like that and expect it do be rendered with the JSX you are returning from the function body.

    Move the modal JSX into the render lifecycle method, replacing the "Show Modal" text.

    class ContextMenu extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          xPos: "0px",
          yPos: "0px",
          showMenu: false
        };
      }
    
      componentDidMount() {
        document.addEventListener("click", this.handleClick);
        document.addEventListener("contextmenu", this.handleContextMenu);
      }
    
      componentWillUnmount() {
        document.removeEventListener("click", this.handleClick);
        document.removeEventListener("contextmenu", this.handleContextMenu);
      }
    
      handleClick = (e) => {
        if (this.state.showMenu) this.setState({ showMenu: false });
      };
    
      handleContextMenu = (e) => {
        e.preventDefault();
    
        this.setState({
          xPos: `${e.pageX}px`,
          yPos: `${e.pageY}px`,
          showMenu: true
        });
      };
    
      alertClicked = () => {
        // do something when clicked?
      };
    
      render() {
        const { showMenu, yPos, xPos } = this.state;
        if (showMenu) {
          return (
            <div
              className="menu-container"
              style={{
                top: yPos,
                left: xPos,
                zIndex: 9,
                position: "absolute"
              }}
            >
              <ListGroup>
                <ListGroup.Item action onClick={this.alertClicked}>
                  <Modal show={true}>
                    <Modal.Header closeButton>
                      <Modal.Title>Modal Title</Modal.Title>
                    </Modal.Header>
                  </Modal>
                </ListGroup.Item>
              </ListGroup>
            </div>
          );
        } else return null;
      }
    }
    

    It occurs to me that you may be trying to open the modal from the context menu. In this case add a separate showModal state that is toggled true in the alertClick handler. Use the showModal state to control the open prop of Modal.

    class ContextMenu extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          xPos: "0px",
          yPos: "0px",
          showMenu: false
        };
      }
    
      componentDidMount() {
        document.addEventListener("click", this.handleClick);
        document.addEventListener("contextmenu", this.handleContextMenu);
      }
    
      componentWillUnmount() {
        document.removeEventListener("click", this.handleClick);
        document.removeEventListener("contextmenu", this.handleContextMenu);
      }
    
      handleClick = (e) => {
        if (this.state.showMenu) this.setState({ showMenu: false });
      };
    
      handleContextMenu = (e) => {
        e.preventDefault();
    
        this.setState({
          xPos: `${e.pageX}px`,
          yPos: `${e.pageY}px`,
          showMenu: true,
          showModal: false
        });
      };
    
      alertClicked = () => {
        this.setState({ showModal: true });
      };
    
      render() {
        const { showMenu, yPos, xPos } = this.state;
        if (showMenu) {
          return (
            <div
              className="menu-container"
              style={{
                top: yPos,
                left: xPos,
                zIndex: 9,
                position: "absolute"
              }}
            >
              <ListGroup>
                <ListGroup.Item action onClick={this.alertClicked}>
                  <Modal show={this.state.showModal}>
                    <Modal.Header closeButton>
                      <Modal.Title>Modal Title</Modal.Title>
                    </Modal.Header>
                  </Modal>
                </ListGroup.Item>
              </ListGroup>
            </div>
          );
        } else return null;
      }
    }