Now that we created the model that uses the DB to serve the data, it’s time to use it!
First, I’m going to manually add the houses data we had in the file to the database, creating 2 rows in the houses table:
Now, the houses.js
file is imported by 2 files:
pages/houses/[id].js
pages/index.js
We need to change this reliance on the file, and use a new API instead.
Let’s work on the list page first.
Houses list
Instead of loading the houses from the JSON file, we’re going to use the House model.
We can use the model inside the getServerSideProps()
method. Next.js becomes an hybrid between frontend and backend.
Let’s start from pages/index.js
.
We can import the model:
import { House as HouseModel } from '../model.js'
Then in getServerSideProps()
we get the houses using:
const houses = await HouseModel.findAndCountAll()
and we return the value iterating over the results, getting the dataValues
property, which returns a plain JS object with the data we need:
return {
props: {
nextbnb_session: nextbnb_session || null,
houses: houses.rows.map((house) => house.dataValues),
},
}
Now we get houses
as a prop in the Home component:
export default function Home({ nextbnb_session, houses }) {
//...
and we need to do a little refactoring of how we print the JSX. We need to embed the content
inside the JSX, otherwise it can’t access the value of the prop, like this:
import House from '../components/House'
import Layout from '../components/Layout'
import Cookies from 'cookies'
import { useStoreActions } from 'easy-peasy'
import { useEffect } from 'react'
import { House as HouseModel } from '../model.js'
export default function Home({ nextbnb_session, houses }) {
const setLoggedIn = useStoreActions((actions) => actions.login.setLoggedIn)
useEffect(() => {
if (nextbnb_session) {
setLoggedIn(true)
}
}, [])
return (
<Layout
content={
<div>
<h2>Places to stay</h2>
<div className='houses'>
{houses.map((house, index) => {
return <House key={index} {...house} />
})}
</div>
<style jsx>{`
.houses {
display: grid;
grid-template-columns: 49% 49%;
grid-template-rows: 300px 300px;
grid-gap: 2%;
}
`}</style>
</div>
}
/>
)
}
export async function getServerSideProps({ req, res, query }) {
const cookies = new Cookies(req, res)
const nextbnb_session = cookies.get('nextbnb_session')
const houses = await HouseModel.findAndCountAll()
return {
props: {
nextbnb_session: nextbnb_session || null,
houses: houses.rows.map((house) => house.dataValues),
},
}
}
The house detail
We do the same in pages/houses/[id].js
.
We import the model:
import { House as HouseModel } from '../../model.js'
Then we use it to get the house by id:
const house = await HouseModel.findByPk(id)
and we can return its dataValues
in the return object:
return {
props: {
house: house.dataValues,
nextbnb_session: nextbnb_session || null,
},
}
In this case we were already returning a house
prop, so nothing needs to be changed.
Here is the full source code:
import Head from 'next/head'
import Layout from '../../components/Layout'
import DateRangePicker from '../../components/DateRangePicker'
import { useState, useEffect } from 'react'
import { useStoreActions } from 'easy-peasy'
import Cookies from 'cookies'
import { House as HouseModel } from '../../model.js'
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')
const house = await HouseModel.findByPk(id)
return {
props: {
house: house.dataValues,
nextbnb_session: nextbnb_session || null,
},
}
}
You can now remove the houses.js
file.
The code for this lesson is available at https://github.com/flaviocopes/airbnb-clone-react-nextjs-2020/tree/6-2