Airbnb clone, determine if we are logged in

Join the 2022 Full-Stack Web Dev Bootcamp!


This post is part of a new series where we build a clone of Airbnb with Next.js. See the first post here.

Great! Now we have the session information stored in the nextbnb_session cookie

Now I want to get the cookie value from the server on the first page load we do, and we store the login state.

Remember the store.js file we created?

Add a new store value:

export default createStore({
  login: {
    loggedIn: false,
    setLoggedIn: action((state) => {
      state.loggedIn = true
    }),
  },
  modals: {
    //...

Now we need to add some code to pages/index.js and pages/houses/[id].js, the 2 entry points.

In pages/index.js:

//at the top
import Cookies from 'cookies'

//at the bottom
export async function getServerSideProps({ req, res, query }) {
  const cookies = new Cookies(req, res)
  const nextbnb_session = cookies.get('nextbnb_session')

  return {
    props: {
      nextbnb_session: nextbnb_session || null
    }
  }
}

This makes the nextbnb_session prop available in the component.

We use useEffect now to check if this prop is available, and if so, we are going to call the setLoggedIn state action:

//at the top
import { useStoreActions } from 'easy-peasy'
import { useEffect } from 'react'

//...
export default function Home({ nextbnb_session }) {
  const setLoggedIn = useStoreActions((actions) => actions.login.setLoggedIn)

  useEffect(() => {
    if (nextbnb_session) {
      setLoggedIn(true)
    }
  }, [])

  //...

We do the same in pages/houses/[id].js. Here is the full code of the component:

import Head from 'next/head'
import houses from '../../houses.js'
import Layout from '../../components/Layout'
import DateRangePicker from '../../components/DateRangePicker'
import { useState, useEffect } from 'react'
import { useStoreActions } from 'easy-peasy'
import Cookies from 'cookies'

const calcNumberOfNightsBetweenDates = (startDate, endDate) => {
  const start = new Date(startDate) //clone
  const end = new Date(endDate) //clone
  let dayCount = 0

  while (end > start) {
    dayCount++
    start.setDate(start.getDate() + 1)
  }

  return dayCount
}

export default function House({ house, nextbnb_session }) {
  const [dateChosen, setDateChosen] = useState(false)
  const [numberOfNightsBetweenDates, setNumberOfNightsBetweenDates] =
    useState(0)

  const setShowLoginModal = useStoreActions(
    (actions) => actions.modals.setShowLoginModal
  )

  const setLoggedIn = useStoreActions((actions) => actions.login.setLoggedIn)

  useEffect(() => {
    if (nextbnb_session) {
      setLoggedIn(true)
    }
  }, [])

  return (
    <Layout
      content={
        <div className="container">
          <Head>
            <title>{house.title}</title>
          </Head>
          <article>
            <img src={house.picture} width="100%" alt="House picture" />
            <p>
              {house.type} - {house.town}
            </p>
            <p>{house.title}</p>
          </article>
          <aside>
            <h2>Choose a date</h2>
            <DateRangePicker
              datesChanged={(startDate, endDate) => {
                setNumberOfNightsBetweenDates(
                  calcNumberOfNightsBetweenDates(startDate, endDate)
                )
                setDateChosen(true)
              }}
            />

            {dateChosen && (
              <div>
                <h2>Price per night</h2>
                <p>${house.price}</p>
                <h2>Total price for booking</h2>
                <p>${(numberOfNightsBetweenDates * house.price).toFixed(2)}</p>
                <button
                  className="reserve"
                  onClick={() => {
                    setShowLoginModal()
                  }}
                >
                  Reserve
                </button>{' '}
              </div>
            )}
          </aside>

          <style jsx>{`
            .container {
              display: grid;
              grid-template-columns: 60% 40%;
              grid-gap: 30px;
            }

            aside {
              border: 1px solid #ccc;
              padding: 20px;
            }
          `}</style>
        </div>
      }
    />
  )
}

export async function getServerSideProps({ req, res, query }) {
  const { id } = query
  const cookies = new Cookies(req, res)
  const nextbnb_session = cookies.get('nextbnb_session')

  return {
    props: {
      house: houses.filter((house) => house.id === parseInt(id))[0],
      nextbnb_session: nextbnb_session || null
    }
  }
}

Now we need to change components/Header.js to show a “Logged in” state in the header instead of the links to login and signup.

We first import

import { useStoreState, useStoreActions } from 'easy-peasy'

and inside the component we define

const loggedIn = useStoreState((state) => state.login.loggedIn)
const setLoggedIn = useStoreActions((actions) => actions.login.setLoggedIn)

Finally we wrap the <nav> markup we already have with this JSX:

{
  loggedIn ? (
    <nav>
      <ul>
        <li>
          <a>Logged in</a>
        </li>
      </ul>
    </nav>
  ) : (
    <nav>
      <ul>
        <li>
          <a href="#" onClick={() => setShowRegistrationModal()}>
            Sign up
          </a>
        </li>
        <li>
          <a href="#" onClick={() => setShowLoginModal()}>
            Log in
          </a>
        </li>
      </ul>
    </nav>
  )
}

See the code on GitHub

Next part: Change state after we login

Want to become a better Web Developer? Join the 2022 Web Development Bootcamp!

⭐️⭐️⭐️ Join the 2022 Web Development Bootcamp ⭐️⭐️⭐️