Uygulamalarla Next.js ile Sunucu Taraflı Render (SSR)

React’ın temelinde JavaScript olduğu için sayfalarımız komutlarla dizayn ediliyor. Yan klasik HTML5 etiketleri ve CSS 3 kodları kullanmıyoruz. React’ın JSX mimarisi ile tüm içerikler ve stiller JavaScript tarafından oluşturuluyor. Üretilen sayfaların kodlarına da baktığımızda sadece JavaScript kodları görürüz. Biz nasıl kodları görüyorsak, arama motorları da kodları görür ve o sayfada ne var ne yok çok da anlamaz. Aslında yakın zamanda SEO sorunu ile ilgili gelişmeler oldu ve SPA yapısındaki sayfaların içerikleri de indekslenmeye başladı. Fakat yine de SEO için sorunsuz bir sayfa tasarımı yapmak istiyorsak, istemci tarafında render (CSR) değil de sunucu taraflı render (SSR) yaparak istemciye saf HTML5 etiketlerini göndermek daha doğru bir yöntemdir. React, CSR’dir, fakat Next.js ile birlikte entegre edildiğinde SSR yapabiliriz. Bu bölümde de her ikisini bir arada kullanarak basitçe Next.js’i nasıl kullanacağımızı göreceğiz.

Next.js Nedir ve Ne Amaçla Kullanılır

CSR yapısı ile yapılan uygulamalar doğrudan NPM üzerinden yayınlanabileceği gibi (domain yönlendirilmesi lokal makinaya yönlendirilir) ve web’de yayına açılabilir. Veya kitabın ilk giriş konularında gördüğümüz doğrudan statik paketleme yöntemi ile de paketlenebilir ve rahatlıkla dağıtıma açılabilir. Paketlemede Babel kullanımı ile de JSX sözdizimi rahatlıkla tarayıcı tarafından yorumlanabilir. Fakat hiç bu riske girmek istemez ve özellikle de SEO dostu web projeleri gerçekleştirmek için SSR ile dağıtım yapmak daha uygundur. React’ta günümüzde ve daha öncesinde de popüler olarak Next.js ile SSR yapılmaktadır. https://nextjs.org/ adresinde daha detaylı bilgi edinebilirsiniz, ancak bu bölüm boyunca da Next.js ile hemen hemen tüm özelliklerini kullanarak basit bir uygulama örneği üzerinde pratik yapacağız.

Yeni React-Next.js Projesi Başlatmak

Daha önceki makalelerimizde create-react-app reposu ile projelerimizi başlatıyorduk. Next.js ile yapılan uygulamaların dosya mimarisi klasik React dosya mimarisinden biraz farklıdır. Bu nedenle Next.js’in kendi reposundan faydalanacağız. Aradaki farkı da ilerleyen konularda göreceksiniz.

Aşağıdaki komut ile repoyu indirin.

npx create-next-app <proje-ismi>

Proje ismi olarak react-ssr adını girdik. Dosya yüklemesi tamamlandıktan sonra ./react-ssr klasörüne girin ve projeyi ayağa kaldırın.

cd react-ssr
npm run dev

Projeyi localhost:3000’de ayağa kaldırdığınızda, aşağıdaki gibi bir sayfa şablonu ile karşılaşacaksınız.

Ek bilgi; eğer Next.js sunucusunu durdurmak isterseniz aşağıdaki komutları kullanabilirsiniz. Windows için açık olan 3000 port’unu görmek ve ID ile port’u durdurmak için:

netstat -ano | findstr :3000
tskill <portID>

SSR Dosya Mimarisinin İncelenmesi

SSR ile CSR arasında fark olduğu gibi iki yapı arasındaki dosyalar arasında da fark vardır. React’ta SPA uygulamaları geliştirirken çeşitli Router modüllerinden faydalanmış ve BrowserRouter ile de hangi path ile hangi Component’in render olması gerektiğini tanımlamıştık. Next.js’te ise durum farklıdır. Hem farklı hem de sanki işleri biraz daha basitleştiriyormuş gibi bir durum var. Nasıl ki bilgisayarda klasörler arasında gezinti yaparken adresleme rahatlıkla yapılabiliyorsa, Next.js de bu yöntemi benimsemiş. Öncelikle create-next-app ile oluşturduğumuz projenin içinde neler varmış bir bakalım…

Görüldüğü üzere ./node_modules haricinde 3 adet kök klasör bulunuyor. Bunlar; pages, public ve styles klasörleri.

Next.js ile Router yapısı kurulurken doğrudan ./pages/ klasörü içinde klasörler ve dosyalar aranır. Var olan standart şablonda ./pages/api/hello şeklinde bir path uygulanmış. Hatta npm run dev ile projeyi ayağa kaldırıp, bu path ile sayfayı tarayıcıda açtığımızda aşağıdaki sayfayı görürüz.

./pages klasörü içinde iki adet özel dosya bulunmaktadır.

_app.js: Sayfaların görüntülenmesi için gerekli olan built-in bir dosyadır. İçeriği aşağıdaki gibi basittir.

import '../styles/globals.css'function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp

İncelediğimizde, global olarak tanımlanan stilleri alıp işleyerek parametre olarak gelen Component ve prop’ları geri döndürmektedir. Bir nevi ara katman gibi düşünülebilir.

index.js: İlk açılan sayfadır, Component’tir. Bölüm başında gördüğümüz “Wellcome to Next.js” yazısının göründüğü sayfadır.

./public klasörü içinde de index.html tarafından kullanılan favicon.ico ve vercel.svg bulunuyor. Şablonu temizlemek istediğimizde bunlardan vercel.svg’yi silebiliriz. Bu doya, index.html’de gösterilen footer alanındaki üçgen ikonudur.

./styles klasörü içinde de tüm sayfaları kapsayan global stil dosyası globa.css yer alır. Bu dosya _app.js tarafından yüklenir; body, html, a ve * (hepsi) gibi etiketlerin stillerini kapsar. Her yerde kullanılacak olan genel stiller bu dosyada tanımlanabilir.

Ek olarak bir de Home.module.css dosyası yer alır. Next.js ile CSS dosya isim formatı da bu şekilde oluyormuş, biz de bu yapıya uyarak diğer sayfalar için css’leri tanımlayabilir veya doğrudan Component içinde stilleri oluşturabiliriz. Bu dosya da, yine ilk sayfada gördüğümüz içeriği düzenleyen CSS’leri barındırır. Home, yani index sayfasında kullanılıyor. index.js içinde aşağıdaki gibi içe çekilmektedir.

import styles from '../styles/Home.module.css'

Bundan sonra yapacağımız işlemlerde şablonu temizlemek için bahsettiğimiz dosyalardan gerekli gördüklerinizin içeriğini temizleyerek veya silerek projelerinize başlayabilirsiniz.

Şimdi Router, BrowserRouter veya Switch kullanmadan sadece Next.js ile basit bir SPA uygulamasını nasıl yapacağımızı göreceğiz. Next.js şablonunu incelerken ./page klasörü altında alt klasörler açarak sayfaları tanımlamamız gerektiğini görmüştük. Bu mantıkla birkaç sayfa oluşturalım…

Öncelikle fazlalıkları temizleyerek işe başlıyoruz. Var olan şablonda ilk sayfayı Home olarak tanımlamışlar ama bu ismi beğenmedim, bunun yerine Index kullanmak daha mantıklı görünüyor. Bu nedenle dosya isimlerini düzenleyip, import edildikleri kısımları da bu isimlerle yeniden yazalım.

./pages klasörü altında alt sayfalar olarak about, products, contact isimleri ile alt klasörleri oluşturup, bunların içeriklerini sadece basit metinlerle belirterek hazırlayalım.

Yukarıdaki şekilde görüldüğü üzere klasörleri ve her bir sayfa klasörü altında da index.js dosyası oluşturduk. Tarayıcıya bir path girildiğinde varsayılan olarak o path altında index.js dosyası arar. Örneğimizde http://localhost:3000/about veya http://localhost:3000/about/ adresine girildiğinde doğrudan bu path altındaki index.js dosyası okunur.

Her bir sayfa için şu an aynı kalıpta örnek hazırlandı. Örneğin About sayfası için aşağıdaki kodları inceleyin.

./pages/about/index.js

import styles from '../../styles/About.module.css';export default function About() {
return (
<div className={styles.About} >
<h1>About sayfası</h1>
</div>
)
}

Basitçe bir fonksiyon Component oluşturuldu ve export default ile dışarı çıkarıldı. Dikkat edin, önceki konularda görmüş olduğumuz gibi React ve { Component} içe çekilmemiş. Sadece stilleri, kendi ismi ile css dosyasından import ile alıp styles değişkenine aktarmışız. Bu değişkeni de className’de kullandık. Kullanırken, her bir CSS class ismini bir metot gibi kullandık. Aşağıdaki CSS’i de inceleyin.

.About{ text-align: center; }

Burada, .About bir metot gibi kullanılmakta ve className içine aktarılmaktadır.

Sabit İçerikler ile Düzen (Layout) Sağlamak

Herhangi bir Router yapısı kullanmadığımız için sabit olan sayfaları göstermek için farklı bir metot geliştirmemiz gerekecek. Mesela her sayfada görüntülenmesi gereken menü, üst kısım, alt kısım gibi alanları görüntülemek için ya her bir Component’te tek tek alt Component olarak çekip göstermemiz gerekecek, ya da daha pratik bir çözüm üretmemiz gerekecek. Yapılan pratik yöntemde bir ara katman Component kullanılıp, onda sabit olan Component’ler ile birlikte, parametre olarak gelen asıl Component render edilmektedir. Bu ara katman da, kök klasörde bulunan _app.js’tir. Öncelikle üst kısmı oluşturacak <Header /> Component’ini oluşturalım. Bu Component bir sayfa olmadığı için ./pages klasörü içinde yapmamız gerekecek. Mesela ./layout diye bir klasör olabilir.

./layout/header/index.js

import styles from '../../styles/Header.module.css';export default function Header() {
return (
<div className={styles.Header} >
Header
</div>
)
}

Ve yine kendisi için de basitçe bir CSS yapalım.

./styles/Header.module.css

.Header{ height: 50px; background-color: chocolate; }

Basitçe bir yükseklik verip arkaplanını da çikolata rengi yaptık.

Aynı mantıkla bir de ./layout/footer/index.js dizininde <Footer /> hazırlayın.

import styles from '../../styles/Footer.module.css';export default function Footer() {
return (
<div className={styles.Footer} >
@2020 ugurgelisken.com
</div>
)
}

./styles/Footer.module.css

.Footer{
height: 20px;
text-align: center;
font-size: 10px;
padding: 8px 4px 0px 4px;
background-color: darkgray;
color: black;
position: fixed;
bottom: 0px;
left: 0px;
right: 0px;
}

Sayfanın altına sabitleyip biraz da görsel olarak toparladık.

Evet, şimdi bu iki Component’i sabit içerik olarak _app.js’te ekleyelim.

import '../styles/globals.css'
import Header from '../layout/header/index'
import Footer from '../layout/footer/index'
function MyApp({ Component, pageProps }) {
return (
<>
<Header />
<Component {...pageProps} />
<Footer />
</>
)
}
export default MyApp

Ekleyeceğimiz <Header /> ve <Footer /> Component’lerini import ile içe çektik. Daha sonra da <> ve </> ile isimsiz etiket açıp tüm Component’leri sarmaladık. İsterseniz burada <div> gibi bir etiket içinde de sarmalayabilirsiniz. Ek olarak biraz da iç kısmı gösteren Index.module.css’te biraz CSS düzeltisi yaptık.

.Index{
padding: 10px;
background-color: rgb(248, 248, 248);
overflow: hidden;
height: calc(100vh - 90px);
}

Nihayetinde sayfamız aşağıdaki gibi görünecek. Örnek olması amacıyla /contact/ sayfasına bakılmaktadır. Şu an menü olmadığı için adresi tarayıcıda elle giriyoruz. Tarayıcıda Developer Tools’ta Elements sekmesine baktığımızda; Next.js ile render edildiği için artık JavaScript kodları değil de doğrudan saf HTML5 kodları ve yazdığımız içerikler geliyor.

404 Sayfası

Next.js’te standart olarak şablon içinde bir 404 sayfası bulunur. Eğer URL bulunamazsa, aşağıdaki gibi bir şablon Component render eder.

Özel bir 404 sayfası yapmak için ./pages/ klasörü altında 404.js dosyası oluşturalım. Next.js bir sayfayı bulamazsa, direkt olarak bu dosyayı okur.

./pages/404.js

import styles from '../styles/PageNotFound_404.module.css'export default function PageNotFound_404() {
return (
<div className={styles.PageNotFound_404} >
<h3>Sayfa Bulunamadı</h3>
<p>Ana sayfaya dönmek için <a href="/" >tıklayın.</a></p>
</div>
)
}

Basitçe bir stil verelim.

./styles/PageNotFound_404.module.css

.PageNotFound_404{ text-align: center; padding-top: 20vh; }

Header Etiketlerini Düzenlemek

Normalde her bir sayfanın <head></head> etiketleri bulunur ve o sayfanın bazı kimlik bilgileri yer alır. Mesela meta etiketleri, sayfa başlığı gibi şeyler… Örnek projemizde hazırladığımız sayfalara (404 dahil) sayfa üst bilgileri eklemeyi göreceğiz. Örnek olması amacıyla _app, about ve 404 sayfaları üzerinde gösterelim.

Next.js’te Component’lere üst bilgi eklemek için Head modülünü next/head içinden çekmemiz gerekiyor. Her bir Component’te bu işlemi yapın.

import Head from 'next/head'

İlk olarak _app.js içinde yapacağız. Burada tanımlayacağımız <header></header> bilgileri global olarak tüm sayfalarda kullanılacak.

./pages/_app.js

import '../styles/globals.css'
import Head from 'next/head'
import Header from '../layout/header/index'
import Footer from '../layout/footer/index'
function MyApp({ Component, pageProps }) {
return (
<>
<Head>
<meta charSet="utf-8" />
<title>Merhaba Next.js</title>
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
<meta name="description" content="React ve Next.js ile
SPA denemeleri" />
<meta name="keywords" content="react, spa, next.js, uğur,
gelişken" />
<meta name="author" content="Uğur GELİŞKEN" />
</Head>
<Header />
<Component {...pageProps} />
<Footer />
</>
)
}
export default MyApp

Açılış sayfasında, yani _app.js’te aşağıdaki gibi <head></head> etiketleri içinde <meta /> etiketleri görülecektir. Etiketleri eklerken dikkatli bakarsanız UTF-8’i tanımladığımız char-set’i doğrudan camelCase olarak tanımladık. Yani React’ta bu şekilde de tanımlamalar yapabilirsiniz.

About ve 404 sayfalarında da sadece keyword ve description’ları değiştireceğiz.

./pages/about/index.js

import styles from '../../styles/About.module.css'
import Head from 'next/head'
export default function About() {
return (
<>
<Head>
<title>About</title>
<meta name="description" content="about sayfası ile ilgili açıklama..." />
<meta name="keywords" content="about, react, next" />
</Head>
<div className={styles.About} >
<h1>About sayfası</h1>
</div>
</>
)
}

Yazılan etiketlere bakalım… about.js’te yazdığımız etiketler eğer _app.js’te yazdığımız etiketler ile aynı ise onları ezer ve yenilerini ekler. Diğerleri de olduğu gibi kalır.

404 sayfasında, arama motorlarının bu sayfayı takip etmemesi için farklı bir meta etik

./pages/404.js

...
<Head>
<title>404 : Sayfa bulunamadı</title>
<meta name="robots" content="follow, noarchive, noindex" />
</Head>
...

Yazdığımız tanımlamalar sonucunda, bulunamayan herhangi bir sayfanın etiketleri aşağıdaki gibi güncellenecektir.

Next.js’te Component İçinde Stil Kullanmak

Normalde CSS’leri ayrı modüller olarak tutmak ve dışarıdan içe çekmek doğru bir yöntemdir. Ancak bazı hallerde doğrudan Component içinde stilleri tutmak gerekebilir. Next.js’te normal JSX’te olduğu gibi değil de yine ona benzer bir yapı ile CSS yazılabilir. Bunu bir örnek üzerinde inceleyelim. Örnek olarak /about sayfasında işlem yapalım.

Next.js’te stiller <style jxs></style> içinde Template yapısı ile yapılmaktadır.

./pages/about/index.js

export default function Index() {
return (
<>
<div className={styles.Index} >
<p>Merhaba Next.js!</p>
</div>
<style jsx>{`
p { color: black; }
div { background: white; }
@media (max-width: 600px) {
p{ color: white; }
div { background: black; }
}
`}</style>
</>
)
}

Örneğimizde hem modül olarak CSS’i kullanmaya devam ettik, hem de yeni stillerle düzenlemeler yaptık. Eğer sayfa genişliği 600px’den azsa, sayfanın arkaplanını beyaz, yazıları siyah olarak gösteriyoruz. Eğer sayfa genişliği 600px’den fazlaysa da renkleri ters çeviriyoruz.

Sayfalar Arası Bağlantıları Kurmak

Sayfalama mantığında normal <a /> (404 sayfasında ana sayfaya yönlendirmek için yapmıştık) etiketi ile yönlendirme yapabileceğimiz gibi, Next.js içinden ‘next/link’ ile <Link /> Component’inden faydalanabiliriz. Bunu, daha önce <Router /> ile bağlantılar kurarken görmüştük.

Örnek olması amacıyla <Header /> Component’i içinde bir menü oluşturacağız. Menüyü oluşturmak için de alt butonları kullanmak için <MenuButton /> ismi ile alt Component’lerden faydalanalım, biraz değişiklik olsun.

Alt Component’ler birer sayfa olmadığı için dosya mimarisinde farklı bir alanda tutmak gerekir. Mesela yine ./layout/menu/button/ dizini iyi gibi görünüyor. Stillerini de Component içinde verelim.

import Link from 'next/link'export default (props) => {
const { name, url } = {...props}
return (
<>
<Link href={url} >{name}</Link>
<style jxs>{`
a { margin: 4px; padding: 12px; background-color: white; border-radius: 8px; color: black; text-decoration: none; line-height: 3; }
a:hover { color: chocolate }
`}</style>
</>
)
}

Menu butonu için oluşturduğumuz Component’in bir ismi yok, normal bir fonksiyon olarak dışarı çıkardık. İstesek diğerleri gibi de yapabilirdik, fakat değişik metotlar görmek iyidir. Ayrıca parametre olarak prop almaktadır. Demek ki dışarıdan bir besleme olacak. Render edilen alana baktığımızda da prop içinden name ve url değerleri alınıp değişken haline getirilmiş. Component’in ilk satırında da <Link /> Component’i içe çekilmiş. Bunu, aynı bir <a /> etiketi gibi gelen parametre değerleri ile kullanıyoruz. Ek olarak da biraz stil veriyoruz.

Hazırladığımız bu Component’i dinamik olarak map() ile <Header /> içinden çoğaltacağız. <Header /> içinde bir statik JSON verisi ile dinamik olarak menüyü oluşturalım.

./layout/header/index.js

import styles from '../../styles/Header.module.css'
import MenuButton from '../menu/button/'
export default function Header() {
const menuData = [
{ name: "Ana Sayfa", url: "/" },
{ name: "Hakkında", url: "about" },
{ name: "Ürünler", url: "products" },
{ name: "İletişim", url: "contact" },
{ name: "X", url: "x" }
]
return (
<div className={styles.Header} >
{ menuData.map(item => (
<MenuButton key={item.url} {...item} />
)) }
</div>
)
}

Menüyü oluşturmak için menuData adında bir Object tanımladık. Toplamda aslında ana sayfa ile birlikte 4 adet path olması gerekiyor, fakat bir de 404 sayfasını görelim diye olmayan bir path tanımladık. Az önce oluşturduğumuz <MenuButton /> Component’ini de map() içinde döndürerek tek tek oluşturduk. Her defasında da sırası gelen menü verisinin doğrudan {…item} ile dağıtarak prop olarak aktardık. Gelen prop değerlerini de nasıl işleyeceğimizi görmüştük. Örnek olması amacıyla Ürünler butonuna tıkladık ve /products sayfasını açtık.

Bu aşamada daha da fazla geliştirme yapmak için isterseniz menü verisi içinde <meta /> etiketleri için parametreleri de ekleyip prop’larla sayfalara aktarabilirsiniz. Her bir sayfada da prop’tan meta verilerini alıp <Head> Component’i içinde yazdırabilirsiniz.

Olmayan /x adresi için Next.js tarafından konsolda bir uyarı verilecektir. Yani kırık link kontrolü yapılmaktadır.

İç İçe ve Dinamik İsimli Bağlantılar

Önceki bölümlerde yapmış olduğumuz örneklerde ürünlere tıkladığımızda tıkladığımız ürünün detay sayfasını rahatlıkla bir :id parametresi ile yönlendirerek görebiliyorduk. Next.js’te bir Router yapısı kullanmadığımız için her bir ürün için tek tek sayfa tasarlamak gibi saçma bir düşünceye girmeyeceğiz. Bunun yerine Next.js’in yönlendirmeleriyle almış olduğumuz dinamik ismi, yani :id gibi bir değeri, normal dosya yapısında da kullanmak olacak.

Mesela ./product/product_1 gibi bir adres olduğunda buradaki id değeri product_1 olmaktadır. Dosya yapısında da öncelikle ./pages/product/ dizinine girmiş olmamız gerekiyor. Burada da dinamik olarak dosya ismi tanımlamak gerekiyor. Bunu da [id].js gibi bir isimlendirme ile yapıyoruz. Göndermiş olduğumuz :id ile dinamik olarak product_1.js gibi o dosya okunacak.

Bahsettiğimiz bilgiler neticesinde dosya yapısı aşağıdaki gibi görünecek.

Bağlantıları da oluştururken tabi bu kurala uygun olarak yönlendirme yapmak gerekecek.

<Link href="/product/[id]" as="/product/product_1" >Ürün 1</Link>

Bağlantıyı verirken iki farklı adres verdiğimizi görüyorsunuz. Adresi href ile verdiğimiz asıl gidilecek olan adrestir, arkaplanda bu işletilir. Görünecek olan adres de as ile verilir, tarayıcıda bu görünür. Yani http://localhost:3000/product/proje_1 adresine gidilir. Next.js ise ./product/’tan sonraki kısmı yani proje_1’i alır ve id olarak değerlendirir. Verdiğimiz id ismi yerine başka isimler de kullanılabilir. Ne isim verdiysek, dosya ismi olarak da [] içinde o ismi kullanmalı, eşleştirmeliyiz.

Next.js’te fetch() işlemlerini yapmak için özel bir LifeCycle metodundan faydalanmamız gerekiyor.

Veri Çekme: getInitialProps()

Ürünler sayfasında bir JSON veriyi fetch() ile çekip listeleyip gösterelim. Ürüne tıklayınca da ürünün verisini prop olarak alt sayfaya aktarmak istiyoruz. Bunun içince sayfa render edilmeden önce, sanki componentDidMount() metodu gibi özel bir metot olan getInitialProps() metodunu kullanmamız gerekiyor.

<Products /> Component’i içinde Google’dan bir kelime araması ile ürünleri çekip göstereceğiz. API’den gelen verileri de Component’e prop olarak aktaracağız.

./pages/products/index.js

import styles from '../../styles/Products.module.css'
import { useRouter } from 'next/router'
export default function Products( {items} ) {
const router = useRouter()
const goToProduct = item => {
window.product = item;
router.push({
pathname: '/product/[id]',
query: {id: item.id}
});
}

return (
<div className={styles.Products} >
{items.map(item => (
<div className={styles.ProductsBox} key={item.id}
onClick={() => goToProduct(item)} >
<h3>{item.volumeInfo.title}</h3>
<p>{item.volumeInfo.subtitle}</p>
</div>
))}
</div>
)
}
Products.getInitialProps = async () => {
const res = await fetch(`https://www.googleapis.com/books/v1/volumes?q=u%C4%9Furgeli%C5%9Fken`)
const data = await res.json()
return { ...data }
}

Component’in alt kısmında Products nesnesinin, yani ürettiğimiz Component’in ismi ile getInitialProps() fonksiyonunu asenkron olarak çağırıyoruz. Bu metot, Component render olmadan önce çalışacak ve return ile prop döndürecek. İlk olarak Google’dan kitaplar verisi çekip (ugurgelisken araması ile çekildi), res değişkenine Promise olarak aktarıyoruz. Onun da içinden json() metodu ile alıp data değişkenine aktarıyoruz. Onu da rest operatörü ile parçalayıp geri döndürüyoruz. Döndürülen değerler, Component’e prop olarak aktarılacak.

Yönlendirmeyi de bu defa useRouter ile yaptık. Yönlendirme yaparken de ./product/<id> şeklinde yönlendirme oldu. Şu an ./pages/product/[id].js olmadığı için hata yaşanacak ama birazdan onu da hazırlayacağız. Yönlendirilen adres dinamik olacağı için as prop’unda ./product/<id> olarak URL’de yansımasını göstereceğiz. Ek olarak, gönderilen içeriği bu defa window nesnesine aktardık. Aslında burada isterseniz Context kullanımı yapıp oraya da aktarabilirsiniz. Veya doğrudan id’yi parametre olarak gönderilen sayfada alıp, yeniden REST sorguları yaparak yeni verileri alabiliriniz.

<Products /> Component’inin CSS’ini de biraz düzelttik.

./styles/Products.module.css

.Products{}
.ProductsBox{
border: 1px solid black;
border-radius: 4px;
background-color: blanchedalmond;
text-align: center;
cursor: pointer;
margin: 4px;
}
.ProductsBox:hover{ background-color: chocolate; color: white; }

Sayfayı çalıştırdığımızda, aşağıdaki şekilde görüldüğü gibi ürünler kutular halinde gösterilecek. Her birine tıkladığımızda yeni sayfada detaylarını göreceğiz.

Gönderilen verileri alan <Product /> Componentin’de de window nesnesinden product verisini alıp gösterelim.

./pages/product/[id].js

const Product = () => {
var productData = {}
if (typeof window!== "undefined"
&& typeof window.product !== "undefined") {
productData = window.product;
window.product = null
}else{
return null
}
return (
<div style={{padding: '10px'}}>
<img style={{float: 'right'}} src={productData.volumeInfo.imageLinks.thumbnail} alt={productData.volumeInfo.title} />
<h1>{productData.volumeInfo.title}</h1>
<h4>{productData.volumeInfo.authors}</h4>
<span>Kategori: {productData.volumeInfo.categories}</span><br/>
<span>Sayfa Sayısı: {productData.volumeInfo.pageCount}</span><br/>
<span>Yayınlanma Tarihi: {productData.volumeInfo.publishedDate}</span><br/>
<span>Fiyat: <b>{productData.saleInfo.listPrice.amount} {productData.saleInfo.listPrice.currencyCode} </b></span><br/>
<button style={{margin:'4px'}} onClick={ () => { window.open( productData.accessInfo.webReaderLink, '_blank') } } >Oku</button>
<button style={{margin:'4px'}} onClick={ () => { window.open( productData.saleInfo.buyLink, '_blank') } } >Satın Al</button>
<p>{productData.volumeInfo.description}</p>
</div>
)
}
export default Product;

Sayfada biraz düzenleme yapmak için inline CSS kullandık.

Uygulama Dağıtımı (Deployment)

Next.js için kurduğumuz paket yöneticisinde farklı build komutları yer almaktadır. Bu komutlar package.json’da scripts kısmında yer almaktadır.

"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},

Geliştirme aşamasındayken npm run dev ile development modunda çalışıyorduk. Paketleme yapmak için de npm run build komutunu kullanmalıyız.

Eğer kodlarınızda bir sorun yoksa, yani hata verecek bir işlem yoksa paketleme yapılır. Paketleme işlemi tamamlanınca aşağıdaki gibi bir rapor çıkacak.

Paketlenmiş production versiyonu .next klasörü içine atılacaktır.

--

--

Full-stack Developer [ UI / UX | JAM Stack | ME(A,R,V)N | LAMP ], Author, Pro Gamer

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Uğur GELİŞKEN

Full-stack Developer [ UI / UX | JAM Stack | ME(A,R,V)N | LAMP ], Author, Pro Gamer