How To Make Popup Modal Appear When Clicking a Link?

GalaXee95 picture GalaXee95 · Jul 9, 2019 · Viewed 10.1k times · Source

I am trying to create a Login Modal Form for an application. However, I want the popup to appear when I click a link versus a button. In other words, when I click the login link in my navbar, I don't want to be redirected to another page entirely. I just want the modal to pop up.

I'm very new to ReactJS, so I'm not sure how to go about this. Could somebody please help me understand how to get this function to work? I'd really appreciate it.

Additionally, if anyone knows of some great resources on how to implement a proper login form, I would also greatly appreciate that. I found a few on CodePen, but none of them really show a clear and approachable way on how to build this component. At least, for a beginner like me.

Located below is my code. Also, if it helps, I provided the link to the site I am currently using as a reference to build this code.

Resource: https://react-bootstrap.github.io/components/modal/

App.js

import React from 'react';
import {BrowserRouter, Route, Switch} from 'react-router-dom'
import Navbar from './components/Navbar/navbar.js';
import Footer from './components/Footer/footer.js';
import Home from './pages/Home/home.js';
import Login from './pages/Login/login.js';
import Languages from './pages/Languages/languages.js';

function App() {
  return (
    <div className="App">
      <BrowserRouter>
        <Navbar/>
          <Switch>
            <Route exact path="/" component={Home}/>
            <Route path="/login" component={Login}/>
            <Route path="/languages" component={Languages}/>
          </Switch>
      </BrowserRouter>
      <Footer />
    </div>
  );
}

export default App;

Navbar.js

import React from 'react';
import { Link } from 'react-router-dom';
import './navbar.css';

const Navbar = () => {
    return (
        <nav className="navbar navbar-expand-sm navbar-dark px-sm-5">
            <div className="container">
                <Link to='/'>
                    <div className="navbar-brand">
                        <i class="fas fa-globe fa-2x"></i>
                    </div>
                </Link>

                <ul className="navbar-nav align-items-right">
                    <li className="nav-item ml-5">
                        <Link to="/login" className="nav-link">
                            Log In
                        </Link>
                    </li>
                    <li className="nav-item ml-5">
                        <Link to="/signup" className="nav-link">
                            Sign Up
                        </Link>
                    </li>
                </ul>
            </div>
        </nav>
    )
}

export default Navbar;

Login.js

import React, { Component } from 'react';
// import { connect } from 'react-redux';
import Modal from 'react-bootstrap/Modal';
import Button from 'react-bootstrap/Button';
import './login.css';

class Login extends Component {
    constructor(props, context) {
        super(props, context);

        this.handleShow = this.handleShow.bind(this);
        this.handleClose = this.handleClose.bind(this);

        this.state = {
          show: false,
        };
      }

      handleClose() {
        this.setState({ show: false });
      }

      handleShow() {
        this.setState({ show: true });
    }

    render() {
        return (
            <>
                <Button variant="primary" onClick={this.handleShow}>
                  Launch demo modal
                </Button>

                <Modal show={this.state.show} onHide={this.handleClose}>
                  <Modal.Header closeButton>
                    <Modal.Title>Login</Modal.Title>
                  </Modal.Header>
                  <Modal.Body>...</Modal.Body>
                  <Modal.Footer>
                    <Button variant="danger" onClick={this.handleClose}>
                      Cancel
                    </Button>
                  </Modal.Footer>
                </Modal>
            </>
        );
    }
}

export default Login;

Answer

Cool Guy picture Cool Guy · Jul 9, 2019

Let's refactor your Navbar to be a class-component instead. We're going to need to keep track of state and pass down a binded function to the Login modal.

Additonally, it looks like you won't need a Login page anymore, so let's extract that markup so that its in a component instead. We'll call it LoginModal

Navbar.js

import React from "react"
import { Link } from 'react-router-dom';
import './navbar.css';
import LoginModal from "./components/LoginModal"


class Navbar extends React.Component{
  state = {
     modalOpen: false
  }

  handleModalOpen = () => {
     this.setState((prevState) => {
        return{
           modalOpen: !prevState.modalOpen
        }
     })
  }

  render(){
    return (
      <div>
        <nav className="navbar navbar-expand-sm navbar-dark px-sm-5">
            <div className="container">
                <Link to='/'>
                    <div className="navbar-brand">
                        <i class="fas fa-globe fa-2x"></i>
                    </div>
                </Link>

                <ul className="navbar-nav align-items-right">
                    <li className="nav-item ml-5">
                        <a onClick={this.handleModalOpen} className="nav-link">
                            Log In
                        </a>
                    </li>
                    <li className="nav-item ml-5">
                        <a onClick={this.handleModalOpen} className="nav-link">
                            Sign Up
                        </a>
                    </li>
                </ul>
            </div>
        </nav>
        <LoginModal
           modalOpen={this.state.modalOpen}
           handleModalOpen={this.handleModalOpen}
        />
      </div>
    )
  }
}

export default Navbar;

Notes about Navbar:

  • It has a component state that keeps track of the status of the modal.
  • The modal is placed right at the end of nav jsx.
  • Replaced the Link components with standard a-tags and gave them an onClick handler
  • The onClick handler, handleModalOpen toggles a value in our state called openModal.
  • openModal and handleModalOpen gets passed down to the LoginModal component.

So now let's refactor Login to be LoginModal.

LoginModal

import React from 'react';
import Modal from 'react-bootstrap/Modal';
import Button from 'react-bootstrap/Button';
import './login.css';

const LoginModal = (props) => { 
    return (
        <>
          <Modal show={props.modalOpen} onHide={props.handleModalOpen}>
              <Modal.Header closeButton>
                 <Modal.Title>Login</Modal.Title>
              </Modal.Header>
              <Modal.Body>...</Modal.Body>
              <Modal.Footer>
                 <Button variant="danger" onClick={props.handleModalOpen}>
                    Cancel
                 </Button>
              </Modal.Footer>
          </Modal>
        </>
     );
}

export default LoginModal;

Notes about LoginModal

  • We were able to remove a lot of the original logic now that LoginModal is strictly just responsible for consuming props and displaying content.
  • We use the prop value, props.modalOpen which is passed down from Navbar, it gets set to true when the button is clicked inside the Navbar component. So show={true} will display the modal
  • Similarly, we use another prop, props.handleModalOpen which toggles the state in the parent component. When you call that function in the modal, it updates state.modalOpen in the parent to false.
  • That updated value gets passed back down to LoginModal, setting props.modalOpen to false, so show={false} thus closing the modal.

Lastly App.js can now just be:

App.js

import React from 'react';
import {BrowserRouter, Route, Switch} from 'react-router-dom'
import Navbar from './components/Navbar/navbar.js';
import Footer from './components/Footer/footer.js';
import Home from './pages/Home/home.js';
import Languages from './pages/Languages/languages.js';

function App() {
  return (
    <div className="App">
      <BrowserRouter>
        <Navbar/>
          <Switch>
            <Route exact path="/" component={Home}/>
            <Route path="/languages" component={Languages}/>
          </Switch>
      </BrowserRouter>
      <Footer />
    </div>
  );
}

export default App;