Search code examples
cssreactjsbootstrap-modalreact-bootstrap

Modal not showing up in react with react-bootstrap


I am trying to create a modal to have a login/register pop-up when clicking on the NavBar.Item Login/Register.

My modal is defined as below:

import React from "react";
import '../assets/styles/GenericTheme.css'
import { Modal } from "react-bootstrap";

class LoginRegisterModal extends React.Component {  
    constructor(props, context) {
        super(props);
        this.state = {show: false};
    }

    open = () => {
        this.setState({show: true});
    }

    close = () => {
        this.setState({show: false});
    }

    render() {  
        return (         
     //   <Modal show={this.state.show} onHide={this.close} dialogClassName="popup-inner">
        <Modal show={this.state.show} fade={false} style={{width: "200px", display: "block"}}>
            <Modal.Body>test</Modal.Body>
        </Modal>
        );  
    }  
}  

export default LoginRegisterModal;

In the NavBar, I have added the reference to the Modal as below:

import React, {}  from 'react';
import { Navbar, Nav, NavDropdown } from 'react-bootstrap';
import SearchForm from '../HeaderFooter/SearchForm';
import SiteLogo from '../../assets/images/village-logo.svg';
import '../../assets/styles/HeaderTheme.css'
import LoginRegisterModal from '../LoginRegisterModal';


class NavHeader extends React.Component {

    constructor(props, context) {
        super(props);
        this.state = {showLogin: false};
    }

    openLogin = () => {
        this.setState({showLogin: !this.state.showLogin});
    }

    render() {
        return (
            <div>
            <Navbar className="village-header" width="100" expand="lg">
                <Navbar.Brand href="/">
                    <img
                        src= { SiteLogo }
                        width="214"
                        height="28"
                        className="d-inline-block align-top"
                        alt="Village"
                    />
                </Navbar.Brand>
                <SearchForm />
                <Navbar.Toggle aria-controls="basic-navbar-nav" />
                <Navbar.Collapse id="basic-navbar-nav">
                    <Nav className="mr-auto village-header">
                    <Nav.Link href="/discover">Discover</Nav.Link>
                    <Nav.Link href="/create">Create</Nav.Link>
                    <Nav.Link href="/howitworks">How it Works</Nav.Link>
                    <Nav.Link ref="LoginRegisterModal" eventKey={1} href="#" onClick={this.openLogin}>Login/Register</Nav.Link>
                    <NavDropdown title="Profile" id="basic-nav-dropdown">
                        <NavDropdown.Item>Firstname LastName</NavDropdown.Item>
                        <NavDropdown.Divider />
                        <NavDropdown.Item href="/profile">Profile</NavDropdown.Item>
                        <NavDropdown.Item href="/messages">Messages</NavDropdown.Item>
                        <NavDropdown.Item href="/settings">Settings</NavDropdown.Item>
                        <NavDropdown.Item href="/logout">Logout</NavDropdown.Item>
                    </NavDropdown>
                    </Nav>
                </Navbar.Collapse>
            </Navbar>
            <LoginRegisterModal show={this.state.showLogin} />
            </div>
            );
        }
}

export default NavHeader;

When I click on the Login/Register:

<Nav.Link ref="LoginRegisterModal" eventKey={1} href="#" onClick={this.openLogin}>Login/Register</Nav.Link>

nothing happens, no pop-up or modal is shown. Also I have added the line below in the NavBar

<LoginRegisterModal show={this.state.showLogin} /> but nothin change. I tried also by adding a className and forcing the z-layer to 2 but it's not working as well.

Any ideas ? Regards


Solution

  • Your modal component isn't "listening" for any passed props, i.e. the show prop being passed from NavHeader. A quick dirty would be to implement the componentDidUpdate lifecycle function to check for the show prop value change. Also use the show prop value to set initial state in the case it's displayed open upon mounting.

    class LoginRegisterModal extends React.Component {  
      constructor(props, context) {
        super(props);
        this.state = {
          show: props.show, // use the initial show value
        };
      }
    
      open = () => {
        this.setState({ show: true });
      }
    
      close = () => {
        this.setState({ show: false });
      }
    
      componentDidUpdate(prevProps) {
        const { show } = this.props;
        if (prevProps.show !== show) {
          if (show) {
            open(); // open if parent says to
          } else {
            close(); // close if parent says to
          }
        }
      }
    
      render() {  
        return (         
        //   <Modal
        //     show={this.state.show}
        //     onHide={this.close}
        //     dialogClassName="popup-inner"
        //   >
          <Modal
            show={this.state.show}
            fade={false}
            style={{width: "200px", display: "block"}}
          >
            <Modal.Body>test</Modal.Body>
          </Modal>
        );  
      }  
    }
    

    This allows the parent to to toggle the modal's display state, but what if the modal is closed/dismissed from within. You may want some way to signal back out to the NavHeader that the modal was closed so it can "sync" its state. The onHide prop does that, and you have it commented out. I suggest also exposing out to the LoginRegisterModal API an onHide prop.

    close = () => {
      this.setState({show: false});
      this.props.onHide();
    }
    

    The the parent uses as such

    <LoginRegisterModal
      show={this.state.showLogin}
      onHide={() => this.setState({ showLogin: false })}
    />
    

    This however, creates code duplication. It may be a better design to make LoginRegisterModal a controlled component and simply pass the show and onHide props passed from NavHeader to the internal Modal component of LoginRegisterModal.

    const LoginRegisterModal = props => (
      <Modal
        // pass props on through to Modal
        {...props}
        // set any props here that you don't want "overridable" by passed props
        fade={false}
        style={{ width: "200px", display: "block" }}
      >
        <Modal.Body>test</Modal.Body>
      </Modal>
    );