When people click the “Reserve” button, we’re now going to show a modal, to let people log in to the site.

Like they do on Airbnb, when you’re not logged in:

Right now we don’t have the option to log in, so people can’t be logged in - we don’t have a lot of logic we must implement. Let’s just show up a modal with the registration form.

Then we’ll add a “Already registered? Login in” link, which will just show the login and password fields.

We’ll skip the workflow to reset the password, which is something you’d need in production, but we can avoid now.

Let’s make the modal form.

We start simple, by creating a test modal. Make a components/Modal.js file, with this content:

components/Modal.js

export default props => (
  <div className='nav-container'>
    <div
      className='modal-background'
      onClick={() => console.log('close')}></div>

    <div className='modal'>{props.children}</div>
    <style jsx global>{`
      .modal-background {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: rgba(0, 0, 0, 0.3);
      }

      .modal {
        position: absolute;
        left: 50%;
        top: 50%;
        width: calc(100vw - 4em);
        max-width: 32em;
        max-height: calc(100vh - 4em);
        overflow: auto;
        transform: translate(-50%, -50%);
        padding: 1em;
        border-radius: 0.2em;
        background: white;
      }
    `}</style>
  </div>
)

This makes a simple modal component, that once imported can be used like this:

<Modal>test</Modal>

and it will render “test” inside a modal.

That’s what we’re going to do.

Open components/Layout.js, add

components/Layout.js

import { useState } from 'react'

import Modal from './Modal'

and then inside the component body, add

components/Layout.js

const [showModal, setShowModal] = useState(true)

and add this in the JSX, after the main tag:

components/Layout.js

{showModal && <Modal>test</Modal>}

This is the result that you should have if you try reloading the page:

Awesome! Now we know the modal is working, in terms of the basic functionality we need.

If you click outside of the modal, nothing happens but you’ll get a “close” string in the console because we have this line:

components/Modal.js

<div
	className='modal-background'
	onClick={() => console.log('close')}></div>

Instead, let’s invoke a close prop, which we assume it’s a function passed to us by the parent component (Layout). We’ll handle closing the modal in that component, by using the setShowModal hook.

components/Modal.js

<div
	className='modal-background'
	onClick={() => props.close()}></div>

components/Layout.js

{showModal && <Modal close={() => setShowModal(false)}>test</Modal>}

Now you should be able to close the modal clicking outside it!

Now let’s create 2 specialized modals: components/RegistrationModal.js and components/LoginModal.js.

In their content, just add

components/RegistrationModal.js

export default () => <p>Registration Modal</p>

and

components/LoginModal.js

export default () => <p>Login Modal</p>

Everything we write inside the opening and closing <Modal> tags will be rendered in the modal, because we used props.children in the Modal component JSX, so just as we entered the test string, we can add another component.

In particular, we can add (depending on our goal) the RegistrationModal or the LoginModal components.

We add 2 more items in the Layout component state, using hooks like we did for showModal:

components/Layout.js

const [showLoginModal, setShowLoginModal] = useState(true)
const [showRegistrationModal, setShowRegistrationModal] = useState(false)

Notice I set showLoginModal to default to true, so we can default to showing the login modal for our testing purposes.

Now in the Layout component JSX instead of

{showModal && <Modal>test</Modal>}

we embed the LoginModal or RegistrationModal components depending on the component state:

{showModal && (
	<Modal close={() => setShowModal(false)}>
		{showLoginModal && <LoginModal />}
		{showRegistrationModal && <RegistrationModal />}
	</Modal>
)}

This should already display the Login component in the modal.

Now let’s define this Login component in details. We want to define a form with an email and password fields:

components/LoginModal.js

export default () => (
  <>
    <h2>Log in</h2>
    <div>
      <form>
        <input id='email' type='email' placeholder='Email address' />
        <input id='password' type='password' placeholder='Password' />
        <button>Log in</button>
      </form>
    </div>
  </>
)

This is just some basic HTML. We need some CSS, but since we are going to use the same CSS in the registration modal too, let’s add the CSS in the Layout component as part of the global styles:

button {
	background-color: rgb(255, 90, 95);
	color: white;
	font-size: 13px;
	width: 100%;
	border: none;
	height: 40px;
	border-radius: 4px;
	cursor: pointer;
}

input[type='text'],
input[type='email'],
input[type='password'] {
	display: block;
	padding: 20px;
	font-size: 20px !important;
	width: 100%;
	border: 1px solid #ccc;
	border-radius: 4px;
	box-sizing: border-box;
	margin-bottom: 10px;
}

Notice how the button stying is the same as the one we added in pages/houses/[id].js to style the “Reserve” button, so we can remove the corresponding CSS from that file, to avoid redundancy.

Things should look pretty good by now:

The registration component is going to be very similar:

export default () => (
  <>
    <h2>Sign up</h2>
    <div>
      <form>
        <input id='email' type='email' placeholder='Email address' />
        <input id='password' type='password' placeholder='Password' />
        <input
          id='passwordconfirmation'
          type='password'
          placeholder='Enter password again'
        />
        <button>Sign up</button>
      </form>
    </div>
  </>
)

Now we add a way to go from one form to another by adding a link at the bottom of each form:

<p>
	Don't have an account yet?{' '}
	<a href='javascript:;' onClick={() => props.showSignup()}>Sign up</a>
</p>

and

<p>
	Already have an account? <a href='javascript:;' onClick={() => props.showLogin()}>Log in</a>
</p>

I used javascript:; as the href value, to tell the browser we’ll use JS to handle the click, and simultaneously avoid the URL to change if I use href="#". See https://flaviocopes.com/links-for-javascript/ for more info on this method I used.

Notice how we call props.showSignup() and props.showLogin(). Those are 2 functions we pass as props from the parent component, Layout.

Don’t forget to change export default () => ... to export default props => ... in LoginModal.js and RegistrationModal.js, because previously we didn’t have any props, but now we must declare we receive the props as a parameter.

Now in Layout.js we handle for each component the corresponding prop:

{showModal && (
	<Modal close={() => setShowModal(false)}>
		{showLoginModal && (
			<LoginModal
				showSignup={() => {
					setShowRegistrationModal(true)
					setShowLoginModal(false)
				}}
			/>
		)}
		{showRegistrationModal && (
			<RegistrationModal
				showLogin={() => {
					setShowRegistrationModal(false)
					setShowLoginModal(true)
				}}
			/>
		)}
	</Modal>
)}

You can try the frontend, you should be able to switch between the forms:

Let’s add a form submit event, by adding an event handler to the onSubmit event on the form tags in both forms:

<form
	onSubmit={event => {
		alert('Log in!')
		event.preventDefault()
	}}>
	...

and in the registration form:

<form
	onSubmit={event => {
		alert('Sign up!')
		event.preventDefault()
	}}>
	...

Here’s the full LoginModal.js component

export default props => (
  <>
    <h2>Log in</h2>
    <div>
      <form
        onSubmit={event => {
          alert('Log in!')
          event.preventDefault()
        }}>
        <input id='email' type='email' placeholder='Email address' />
        <input id='password' type='password' placeholder='Password' />
        <button>Log in</button>
      </form>
      <p>
        Don't have an account yet?{' '}
        <a href='javascript:;' onClick={() => props.showSignup()}>
          Sign up
        </a>
      </p>
    </div>
  </>
)

and here’s the full RegistrationModal.js component:

export default props => (
  <>
    <h2>Sign up</h2>
    <div>
      <form
        onSubmit={event => {
          alert('Sign up!')
          event.preventDefault()
        }}>
        <input id='email' type='email' placeholder='Email address' />
        <input id='password' type='password' placeholder='Password' />
        <input
          id='passwordconfirmation'
          type='password'
          placeholder='Enter password again'
        />
        <button>Sign up</button>
      </form>
      <p>
        Already have an account?{' '}
        <a href='javascript:;' onClick={() => props.showLogin()}>
          Log in
        </a>
      </p>
    </div>
  </>
)

Great!

In the next lesson we’ll link these modals to the rest of the application.

The code for this lesson is available at https://github.com/flaviocopes/airbnb-clone-react-nextjs/commit/8c82646a03ac3bff3ba545edee471b1fd3b9ec5d


Go to the next lesson