6 dk, tahmini okuma süresi
Yeni başlayanlar için harika bir web sitesi projesi. Bu projeyi oluşturduktan sonra, router'ı nasıl kullanacağınızı, dinamik route oluşturmayı ve react.js ile formları nasıl kullanacağınızı öğreneceksiniz. Bu projeyi portfolyonuza ekleyebilirsiniz (tabii ki bazı kısımlarını kişiselleştirip geliştirdikten sonra).
React'in nasıl çalıştığı hakkında en azından biraz bilginiz olduğunu varsayıyorum. Bu yüzden yapacağımız tüm küçük şeyleri açıklamayacağım. Aksi takdirde bu yazı çok sıkıcı ve çok uzun olurdu. İnanın react hakkında fazla bir şey bilmiyor olsanız bile yine de kodu takip edip anlayabileceksiniz. Hadi başlayalım!
Projemizi bu komutla oluşturabiliriz:
npx create-react-app my-hotel-app
Indirme tamamlandığında, konsolda şu komutla:
npx start
projenizi başlatabilirsiniz. Bu noktaya kadar her şey yolundaysa, localhost:3000'e gidin ve yavaşça dönen bir react logosu göreceksiniz.
Projemizde bir çok resim kullanmamız gerekecek. Ücretsiz görseller indirmek için unsplash adresine gidebilirsiniz. Veya repository'mi ziyaret edebilir ve projemde kullandığım görselleri alabilirsiniz. Afişimiz (banner), odalarımız, tesislerimiz vb. için görsellere ihtiyacımız olacak.
Şimdi projemize tailwindcss ekleyelim. Ben projelerimde tailwindcss kullanmayı seviyorum, ancak projelerinizde css başka bir yolla halletmeyi tercih ediyorsanız, bildiğiniz yolla da devam edebilirsiniz. Ama tailwindcss hakkında fazla bir şey bilmiyorsanız, bu kendinizi biraz zorlamanız ve öğrenmeniz adına iyi bir fırsat olabilir.
tailwindcss postcss ve autoprefixer'ı dev dependency olarak indirmek için konsolda bu komutları çalıştırın (bu şekilde uygulamanız aktif iken kullanıcılarınız bu paketleri indirmek zorunda kalmayacaklar):
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
These commands will create a "tailwind.config.js" file in the root of your project. Now change the configs as the following:
Bu komutlar projenizin kök dizininde (root) bir "tailwind.config.js" dosyası oluşturacaktır. Şimdi yapılandırmaları aşağıdaki gibi değiştirin:
module.exports = {
content: [
"./src/**/*.{js,jsx,ts,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
src/index.css dosyasının en üstüne aşağıdaki satırları ekleyin. Bu şekilde projemiz artık tailwindcss kullanmaya hazır olacak:
@tailwind base;
@tailwind components;
@tailwind utilities;
Artık projemizde tailwind css kullanabiliriz. İsterseniz resmi sayfayı kendiniz buradan kontrol edebilirsiniz.
src/App.js dosyasındaki her şeyi silin ve kendinizinkini oluşturmaya başlayın. Ben burada bir functional component oluşturacağım (tamamen kişisel tercihim başka bir sebebi yok). Hooklarla çalışmayı daha çok seviyorum ve bir component'i (bileşeni) çok daha az kodla oluşturmak mümkün. Bazı geliştiriciler routing'i (yönlendirmeyi) index.js dosyasında tutuyor ama ben bu şekilde ayırmayı seviyorum. Devam edin ve src/app.css dosyasını da silin. Css kullanmaızı gerektirecek bir öğe burada olmayacak artık.
Şimdi bir projeye başlamadan önce her zaman eklediğim, temel css'i ele alalım. "padding: 0, margin: 0, box-sizing: border-box" vb. Projenin geri kalanı için burada herhangi bir değişiklik yapmanız gerekmeyeceğinden, burada kodun son halini kullanabiliriz:
@tailwind base;
@tailwind components;
@tailwind utilities;
html, body {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
display: flex;
flex-direction: column;
max-width: 100vw;
min-height: 100vh;
}
#root {
display: flex;
flex-direction: column;
min-height: 100vh;
}
img {
width:100%;
}
root'a "display: flex" değeri vermemiz projemizdeki konumlandırmaları çok kolaylaştıracak. Örneğin, bu şekilde footer için sadece "margin-top: auto" kullanabiliriz ve footer her zaman sayfamızın altında görünür.
Projemizdeki css'in geri kalanı tailwindcss kullanılarak oluşturdum, bu yüzden daha fazla css dosyası olmayacak. Yine, benim kişisel seçimim, kendi yönteminizle devam edebilirsiniz.
Birazdan oluşturacağımız router ve navbar'ı test etmek için gerekli temel sayfaları oluşturabiliriz. Şimdi src'de bir "pages" klasörü oluşturalım. Burada 5 dosya oluşturmamız gerekiyor. Otelin iletişim bilgileri için contact.jsx. Otelimizin sunduğu tüm harika şeyler için tesisler.jsx. home.jsx tabii ki misafirlerimizi karşılamak için. Konuklara oda seçeneklerini gösteren rooms.jsx dosyası. fiyat hesaplama ve rezervasyon için room.jsx.
Bu proje için backend'imiz olmadığından, backend davranışını taklit edeceğiz.
Projeleriniz büyüdükçe, düzinelerce (hatta yüzlerce) bileşen (component) kullanmaya başlayacaksınız. Bu, projelerinizi temiz, okunması kolay tutar ve projelerinizin bazı bölümlerinde küçük değişiklikler yapmayı çok daha kolay hale getirir.
Projemizde yönlendirme yapmak için kullanacağımız router paketini şu komutla kurabiliriz:
npm install react-router-dom
Şimdi navbarımızı oluşturalım. "src" klasörümüzde bir "components" klasörü oluşturun. Projemizde kullanmak istediğimiz tüm gerekli bileşenleri burada oluşturacağız. Burada bir "Navbar.js" dosyası oluşturun. navbarda a tagleri yerine dahili bağlantılar için "Link" kullanmamız gerekiyor (siz bunu yerine Navlink kullanabilirsiniz, ve hatta kullanın). Linklerle alakalı dokümanı burada bulabilirsiniz. Gelecekte react kullanarak çalışmak istiyorsanız kesinlikle okumaya değer. Buradaki linkleri biraz daha düzgün göstermek istediğim için, components klasöründe NavLink.js adında başka bir component oluşturdum. Bu component gerçekten çok basit ve prop olarak da yalnızca link için adres alacak. "to" değeri, bağlantının üzerine tıklayan kullanıcıyı nereye göndereceğini belirler. "a" tagdeki href değeri gibi düşünebilirsiniz.
NavLink.js için kodu burada bulabilirsiniz:
import React from 'react'
import { Link } from 'react-router-dom'
const NavLink = ({to}) => {
return (
<Link className='font-bold m-1 text-xl uppercase' to={`/${to}`} >{to}</Link>
)
}
export default NavLink
Gördüğünüz gibi çok karmaşık bir şey yok, sadece biraz css eklendi. Css sayesinde, metin bölümünde prop olarak göderilen değer bu şekilde büyük harflerle yazılacak.
Navbar.js için kodumuz burada:
import React from 'react'
import { Link } from 'react-router-dom'
import NavLink from './NavLink'
const Navbar = () => {
return (
<nav className='text-white px-8 py-6 bg-cyan-500 flex flex-row justify-between'>
<Link className='font-bold text-2xl' to="/">HOME</Link>
<div>
<NavLink to="rooms"/>
<NavLink to="facilities"/>
<NavLink to="contact"/>
</div>
</nav>
)
}
export default Navbar
Çok temiz ve okunması kolay görünüyor. Her sayfanın uygun bir footera ihtiyacı vardır. Biz de bizim projemize bir tane ekleyeceğiz. Ne kadar basit olursa olsun. Footerı oluşturduktan sonra bir layout bileşeni oluşturacağız ve sayfalarımızı bununla saracağız.
Footer için components klasöründe bir Footer.js dosyası oluşturun. Bu dosyaya ekleyeceğimiz tek şey ismimiz. Footer için tam kod burada:
import React from 'react'
const Footer = () => {
return (<>
<footer className='mt-auto p-10 bg-gray-400'>
<p className='text-center text-white'>Ilker AKBIYIK</p>
</footer>
</>)
}
export default Footer
TAbii siz kendi adınızı ekleyin buraya;).
After creating the footer, create a Layout.js file in components folder and, import both footer and navbar. We will also be using "Outlet" from react-router-dom package. The Outlet will determine where the wrapped compoenents or pages will appear in our layout. In this example we want to wrap our pages with our layout (pages will be under the navbar and above the footer). The code for the Layout.js file is here:
Footer oluşturduktan sonra, components klasöründe bir Layout.js dosyası oluşturun ve hem footerı hem de navbarı buraya aktarın. Ayrıca react-router-dom paketinden "Outlet" kullanacağız. Outletin konumu sarılmış bileşenlerin nereye konumlanacağını belirler. Bu örnekte sayfalarımızı "layout" ile sarmak istiyoruz (sayfalar navbarı altında ve footerın üstünde olacak). Layout.js dosyasının kodu burada:
import React from 'react'
import { Outlet } from 'react-router-dom'
import Navbar from './Navbar'
import Footer from './Footer'
const Layout = () => {
return (<>
<Navbar />
<Outlet />
<Footer />
</>)
}
export default Layout
Şimdi src klasöründe bulunan App.js dosyamızdaki route'ları oluşturalım. Bunu burada açıklamak yerine aşağıdaki yorumlarda kodun işlevini açıklamaya çalışacağım. React projelerimizde yönlendirmeyi ele almanın birden çok yolu olduğunu bilmelisiniz. Ben bu şekilde yaptım:
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import Layout from './components/Layout';
import Home from './pages/home';
import Facilities from './pages/facilities';
import Contact from './pages/contact';
import Rooms from './pages/rooms';
import Room from './pages/room';
function App() {
return (
// Yönlendirmemizi BrowserRouter ile sarmamız gerekiyor
<BrowserRouter>
<Routes>
{/* Öncelikle tüm sayfalarımızı layout ile bu şekilde sarıyoruz */}
<Route path="/" element={<Layout/>}>
{/* home.js "/" adresinde sunulacak */}
{/* index özelliği, aynı adı paylaşan yolda */}
{/* ne sunulacağını belirlemek önemli. */}
<Route index element={<Home />} />
<Route path="contact" element={<Contact/>} />
<Route path="facilities" element={<Facilities/>} />
<Route path="rooms">
{/* Detaylı görünümler için room sayfası dinamik olarak oluşturlacak*/}
<Route path=":id" element={<Room/>} />
<Route index element={<Rooms/>}/>
</Route>
</Route>
</Routes>
</BrowserRouter>
);
}
export default App;
Bu noktaya kadar her şey yolunda gittiyse, navbardaki butonları kullanarak veya doğrudan gitmek istediğiniz url'yi yazarak gezinebilmelisiniz.
Şimdi uygulamamızın biraz daha iyi görünmesini sağlamaya başlayabiliriz. Bazı görselleri indirerek başlayacağız.
Odalar, tesisler ve manzaralar için resimlere ihtiyacımız var. Birkaç resim indirin ve projeye geri dönün. Kendinizi çok tembel hissediyorsanız, projemdeki resimleri buradan alabilirsiniz.
Tamam, şimdi src klasörünün içinde "images" klasörü oluşturun ve resimleri buraya ekleyin.
Projemiz için bir veri tabanımız olsaydı, odalarla ilgili bilgilerimizi oradan alıyor olurduk. Ancak bu proje için bir dosya oluşturarak ve tüm verileri oradan sunarak bu davranışı taklit edeceğiz. src klasörünün içinde "db" adında bir klasör oluşturun. Db içinde "index.js" oluşturun. Bu dosyanın içinde artık odalarımızı oluşturabiliriz. Tam kod burada:
import room1pic from '../images/room1.jpg'
import room2pic from '../images/room2.jpg'
import room3pic from '../images/room3.jpg'
import room4pic from '../images/room4.jpg'
import room5pic from '../images/room5.jpg'
import room6pic from '../images/room6.jpg'
const rooms = [
// isimlendirmeyi ve bilgileri değiştirebiliriniz tabii ki
{ id:1, src: room1pic, info:"A cozy room for 2", name:"Twin", people: 2, dailyPrice:100, availableFor: 7 },
{ id:2, src: room2pic, info:"A cozy room for 2", name:"Desert", people: 4, dailyPrice:150, availableFor: 10 },
{ id:3, src: room3pic, info:"A cozy room for 2", name:"Standard", people: 2, dailyPrice:200, availableFor: 20 },
{ id:4, src: room4pic, info:"A cozy room for 2", name:"Villa", people: 4, dailyPrice:100, availableFor: 5 },
{ id:5, src: room5pic, info:"A cozy room for 2", name:"Open", people: 2, dailyPrice:50, availableFor: 30 },
{ id:6, src: room6pic, info:"A cozy room for 2", name:"Suite", people: 2, dailyPrice:250, availableFor: 14 },
];
export default rooms
Ana sayfamızın içindeki odaları gösteren havalı ve sade bir atlıkarınca (carousel) eklemek istedim. Carousel'i "react-responsive-carousel" paketi ile oluşturdum. Bu paketi kurun ve bileşenler klasörünün içinde bir "Carousel.js" dosyası oluşturun. Bu dosyada carouselimizi ele alacağız. Carouselde kullanacağımız veriler ana sayfadan prop olarak alınacak.
/* eslint-disable jsx-a11y/alt-text */
import React from 'react'
import "react-responsive-carousel/lib/styles/carousel.min.css"; // requires a loader
// İsmini değiştirmem gerek, çünkü component de aynı isimde
import { Carousel as C } from 'react-responsive-carousel';
import { Link } from 'react-router-dom';
// Data bu bileşene gönderilecek
const Carousel = ({rooms}) => {
return (<>
<C className='m-auto md:w-3/5 sm:w-4/5'>
{rooms.map(i => {
return <div>
<img src={i.src} />
<p className="legend">{i.name}</p>
</div>
})}
</C>
<Link className='m-auto text-2xl text-cyan-500' to="/rooms">
Choose your room and make your reservation
</Link>
</>)
}
export default Carousel
Şimdi bu yapıldıktan sonra, tesislerimizi ana sayfamızda gösterecek bir bileşen oluşturalım. Bileşen klasörünün içinde "HomeFacilities.js" dosyasını oluşturun. Bu bileşenin kodu çok basit. Sadece biraz html ve tailwindcss. Tüm bileşeni bir linke dönüştürdüm, böylece bir kullanıcı tıkladığında daha fazla bilgi alabilmeleri için "/facilities" e gönderilecekler. Elbette burada sahip olduğumuz tüm bilgiler Latince, bu nedenle pek anlaşılır değil:
import React from 'react'
import { Link } from 'react-router-dom'
const HomeFacilities = () => {
return (
<Link to="/facilities">
<div className='text-white grid grid-cols-2 gap-4 m-auto mb-6 w-11/12'>
<h2 className='col-span-full text-center text-4xl my-8 underline font-bold text-cyan-500'>Explore the facilities</h2>
<div class="rounded p-8 bg-gradient-to-r from-cyan-500 to-blue-500">
<h3 className='font-bold text-2xl'>POOL</h3>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati error corrupti exercitationem possimus expedita inventore, dolores amet repellat veniam mollitia, est iure praesentium fuga sit architecto quam neque facilis dolorum!</p>
</div>
<div class="rounded p-8 bg-gradient-to-r from-sky-500 to-indigo-500">
<h3 className='font-bold text-2xl'>GYM</h3>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati error corrupti exercitationem possimus expedita inventore, dolores amet repellat veniam mollitia, est iure praesentium fuga sit architecto quam neque facilis dolorum!</p>
</div>
<div class="rounded p-8 bg-gradient-to-r from-violet-500 to-fuchsia-500">
<h3 className='font-bold text-2xl'>RESTAURANT</h3>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati error corrupti exercitationem possimus expedita inventore, dolores amet repellat veniam mollitia, est iure praesentium fuga sit architecto quam neque facilis dolorum!</p>
</div>
<div class="rounded p-8 bg-gradient-to-r from-purple-500 to-pink-500">
<h3 className='font-bold text-2xl'>PRIVATE BEACH</h3>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati error corrupti exercitationem possimus expedita inventore, dolores amet repellat veniam mollitia, est iure praesentium fuga sit architecto quam neque facilis dolorum!</p>
</div>
</div>
</Link>
)
}
export default HomeFacilities
Sonunda home.jsx sayfamız şöyle görünecek:
import React from 'react'
import Carousel from '../components/Carousel'
import HomeFacilities from '../components/HomeFacilities'
import beach from '../images/beach.jpg'
import { Link } from 'react-router-dom'
import rooms from '../db/index';
const Home = () => {
return (<>
<h1 className='text-center text-6xl font-bold text-cyan-500 underline'>Welcome to Our Super Awesome Hotel</h1>
<img className='mt-10 object-cover h-96' src={beach} alt="A beautiful beach" />
<Link to="/rooms"><h2 className='text-center text-4xl my-8 underline font-bold text-cyan-500'>Available Rooms</h2></Link>
<Carousel images={rooms} />
<HomeFacilities />
</>)
}
export default Home
Bu noktaya kadar her şey yolunda gittiyse, çalışan bir carousel (atlıkarınca) ve tesisler için bir bileşen içeren yarı düzgün bir ana sayfa görüyor olmalısınız.
Bu sayfa çok basit olacak. Sadece biraz html. Css burada tailwingcss kullanılarak yapıldı tabii. Sayfayı çok daha kullanışlı hale getireceğini düşündüğünüz bir şey varsa, ekleyin. Gördüğünüz tüm projeleri geliştirmenin çok iyi bir öğrenme yöntemi olduğuna inanıyorum. Öğrenmenizi kesinlikle hızlandıracaktır.
Kodu burada bulabilirsiniz:
import React from 'react'
import pBeach from '../images/pbeach.jpg';
import pool from '../images/pool3.jpg';
import restaurant from '../images/restaurant.jpg';
import spa from '../images/spa.jpg';
import gym from '../images/gym.jpg';
const Facilities = () => {
return (<>
<div className='text-cyan-500 flex flex-col items-center'>
<div className='border-b-2 border-cyan-500 p-2 m-4 w-4/5 gap-4 grid grid-cols-3'>
<h2 className='font-bold col-span-3 text-center text-4xl'>POOL</h2>
<img className='col-span-3 lg:col-span-2' src={pool} alt="pool pic" />
<p className='text-4xl col-span-3 lg:col-span-1'>Lorem ipsum dolor sit amet consectetur adipisicing elit. Doloribus cupiditate quia dolor perferendis, magnam quod rerum reprehenderit, nostrum temporibus sequi suscipit veniam, ea iste ipsum qui delectus enim autem incidunt.</p>
</div>
<div className='border-b-2 border-cyan-500 p-2 m-4 w-4/5 gap-4 grid grid-cols-3'>
<h2 className='font-bold col-span-3 text-center text-4xl'>GYM</h2>
<p className='text-4xl col-span-3 lg:col-span-1'>Lorem ipsum dolor sit amet consectetur adipisicing elit. Doloribus cupiditate quia dolor perferendis, magnam quod rerum reprehenderit, nostrum temporibus sequi suscipit veniam, ea iste ipsum qui delectus enim autem incidunt.</p>
<img className='lg:col-span-2 col-span-3' src={gym} alt="" />
</div>
<div className='border-b-2 border-cyan-500 p-2 m-4 w-4/5 gap-4 grid grid-cols-3'>
<h2 className='font-bold col-span-3 text-center text-4xl'>RESTAURANT</h2>
<img className='lg:col-span-2 col-span-3' src={restaurant} alt="pool pic" />
<p className='text-4xl col-span-3 lg:col-span-1'>Lorem ipsum dolor sit amet consectetur adipisicing elit. Doloribus cupiditate quia dolor perferendis, magnam quod rerum reprehenderit, nostrum temporibus sequi suscipit veniam, ea iste ipsum qui delectus enim autem incidunt.</p>
</div>
<div className='border-b-2 border-cyan-500 p-2 m-4 w-4/5 gap-4 grid grid-cols-3'>
<h2 className='font-bold col-span-3 text-center text-4xl'>PRIVATE BEACH</h2>
<p className='text-4xl col-span-3 lg:col-span-1'>Lorem ipsum dolor sit amet consectetur adipisicing elit. Doloribus cupiditate quia dolor perferendis, magnam quod rerum reprehenderit, nostrum temporibus sequi suscipit veniam, ea iste ipsum qui delectus enim autem incidunt.</p>
<img className='col-span-3 lg:col-span-2' src={pBeach} alt="" />
</div>
<div className='border-b-2 border-cyan-500 p-2 m-4 w-4/5 gap-4 grid grid-cols-3'>
<h2 className='font-bold col-span-3 text-center text-4xl'>SPA</h2>
<img className='col-span-3 lg:col-span-2' src={spa} alt="pool pic" />
<p className='text-4xl col-span-3 lg:col-span-1'>Lorem ipsum dolor sit amet consectetur adipisicing elit. Doloribus cupiditate quia dolor perferendis, magnam quod rerum reprehenderit, nostrum temporibus sequi suscipit veniam, ea iste ipsum qui delectus enim autem incidunt.</p>
</div>
</div>
</>)
}
export default Facilities
Evet, bütün kod bu kadardı.
Misafirlerimizin bizimle iletişim kurarken kendilerini rahat hissetmelerini istiyoruz. Bu yüzden güzel bir form ekleyeceğiz. Bizim için postalamayı (ne de bir veritabanını) işlemek için herhangi bir backend mantığımız olmadığı için, yalnızca bize gönderilen bilgileri console.log() ile göreceğiz ve konuğumuza bir teşekkür mesajı göndereceğiz (alert() ile).
Yorumlar, acemi iseniz biraz zor görünebileceğini düşündüğüm kısımları açıklayacak. contact.jsx (src/pages içinde) sayfası için kod ve açıklaması burada:
import React, { useState } from 'react'
const Contact = () => {
// Fonksyion tabanlı bileşenlerde state böyle yönetiliyor
// Boş dizeler (string) olarak kullanacağımız değerleri başlatıyoruz
// ve dizilerdeki ikinci anahtar kelimeler ile (set...)
// stateleri değiştireceğiz
// aşağıda örnekte görmek daha açıklayıcı olacaktır
const [email, setEmail] = useState("");
const [subject, setSubject] = useState("");
const [text, setText] = useState("");
const handleSubmit = (e) => {
// varsayılan (default) davranış sayfayı yenilemektir, bunu istemiyoruz
e.preventDefault()
// misafiri uyar ve bize gönderilen bilgileri günlüğe "kaydet"
alert("thank you for contacting us, we will get back to you as soon as we can.");
console.log(email, subject, text)
}
return (<>
{/* kullanıcı gönder (bu örnkete SUBMIT), veya Enter tuşuna bastığında */}
{/* handleSubmit fonksiyonunu event argümanıyla çalıştırın */}
<form onSubmit={e => handleSubmit(e)} className='m-auto mb-6 sm:w-4/5 lg:w-3/5'>
<div className='mt-2 flex flex-col'>
<label htmlFor="f-email" >
Your email address:
</label>
<input
// input değeri değiştiğinde, durumu güncelleyin
onChange={(e) => setEmail(e.target.value)}
className='rounded p-4 bg-cyan-100'
type="email"
name="f-email"
/>
</div>
<div className='mt-2 flex flex-col'>
<label htmlFor="f-subject">
Subject
</label>
<input
// input değeri değiştiğinde, durumu güncelleyin
onChange={(e) => setSubject(e.target.value)}
className='rounded bg-cyan-100 p-4'
type="text"
name="f-subject"
/>
</div>
<div className='mt-2 flex flex-col'>
<label htmlFor="f-text">
What would you like to tell us
</label>
<textarea
// input değeri değiştiğinde, durumu güncelleyin
onChange={(e) => setText(e.target.value)}
className='rounded bg-cyan-100 p-4'
name="f-text"
id=""
cols="30"
rows="10"
></textarea>
</div>
<input className='p-4 rounded mt-4 font-bold text-white text-2xl cursor-pointer hover:bg-cyan-700 w-full m-auto bg-cyan-800' type="submit" value="SUBMIT" />
</form>
</>)
}
export default Contact
Odaları içeren sayfamız, form inputuna ve tüm oda detaylarına göre odaları filtrelemek için basit bir form içerecektir. Kullanıcılar yalnızca görmek istedikleri odaları görüntülemeyi bu şekilde seçebilirler. Hiçbir kriter olmadığında tüm odaları sunacağız. Bitmiş kod ve işlevlerin açıklamaları burada:
import React, { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import rooms from '../db';
const Rooms = () => {
// nog, "number of guests" kısaltması
const [nog, setNog] = useState();
const [budget, setBudget] = useState();
const [days, setDays] = useState();
// Bileşen monte edildiğinde tüm odaları oluşturacağız
// ve "filtered" değeri undefined (tanımsız) iken
const [allRooms, setAllRooms] = useState();
// "filtered", filtrelemek istediğimiz parametreleri içerecektir.
const [filtered, setFiltered] = useState(undefined)
useEffect(() => {
// Bileşenler monte edildiğinde
// comonentDidMount'ın eşdeğeri
setAllRooms(rooms);
})
const filterRooms = (e) => {
// yenilemeyi tekrar engelle
e.preventDefault();
// odaları filtreliyoruz
const result = allRooms.filter(room => room.dailyPrice <= budget && room.people >= nog && room.availableFor >= days )
// Filtreleyi ayarla, bunlar bizim görmek istediklerimiz
setFiltered([...result]);
}
const clearFilter = () => {
// Tüm odaları gösteriyoruz
setAllRooms(rooms);
setFiltered(undefined)
}
return (<>
<div className='flex flex-col items-center'>
<form onSubmit={filterRooms} className='min-w-[300px] bg-purple-50 p-4 mt-6' >
<div className='flex flex-col'>
<label htmlFor="nog">Number of Guests (Max)</label>
<input
className='bg-cyan-100 p-2'
type="number"
onChange={(e) => setNog(e.target.value)}
name='nog'
placeholder="1"
max={4}
min={1}
/>
</div>
<div className='flex flex-col'>
<label htmlFor="price">Budget (Daily Max)</label>
<input
className='bg-cyan-100 p-2'
type="number"
name='price'
onChange={(e) => setBudget(e.target.value)}
placeholder="100"
min={100}
max={250}
/>
</div>
<div className='flex flex-col'>
<label htmlFor="days">Days</label>
<input
className='bg-cyan-100 p-2'
type="number"
name='days'
onChange={(e) => setDays(e.target.value)}
placeholder="2"
min={1}
max={30}
/>
</div>
<input
type="submit"
value="FILTER"
className='bg-cyan-700 text-white p-2 mt-2 rounded w-full cursor-pointer hover:bg-cyan-600'
/>
</form>
<button onClick={clearFilter} className='bg-purple-700 text-white p-2 mt-2 rounded w-full cursor-pointer hover:bg-purple-600 max-w-[300px]'>CLEAR FILTER</button>
<div className='grid sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 w-5/6 my-4'>
{/* filtre yoksa, tüm odaları gösterin */}
{allRooms && !filtered ? allRooms.map(r => <Link key={r.id} to={`${r.id}`}>
<div >
<img src={r.src} alt="" />
<p className='text-2xl'>{r.name}</p>
<p>Daily Price: {r.dailyPrice} Money Units</p>
<p>Capacity: {r.people} People</p>
<p>Available For: {r.availableFor} Days</p>
</div>
{/* filtered tanımsız (undefined) değilse filtered'dakileri göster */}
</Link>): filtered ? filtered.map(r => <Link key={r.id} to={`${r.id}`}>
<div >
<img src={r.src} alt="" />
<p className='text-2xl'>{r.name}</p>
<p>Daily Price: {r.dailyPrice} Money Units</p>
<p>Capacity: {r.people} People</p>
<p>Available For: {r.availableFor} Days</p>
</div>
</Link>): ""}
</div>
</div>
</>)
}
export default Rooms
Oda nesnesinin url'si, odaları oluştururken belirlediğimiz id değeri ile dinamik olarak oluşturulacaktır. Bu id değeri url'de görünecektir (örneğin /rooms/2). URL'deki bu id değeri, "veritabanında" istediğimiz odayı bulmak için kullanılacaktır. Odaları filtrelemek için url'den id değerini almak için useParams hook'unu kullanacağız.
Rezervasyondan önce misafir için konaklama maliyetini hesaplamamız gerekiyor. Bir arka backend'imiz olmadığı için, sadece bize gönderilenleri tekrar console.log() içinde göstereceğiz. Tam kod burada:
import React, { useState } from 'react'
import { useParams } from 'react-router-dom';
import rooms from '../db';
const getRoomData = (roomId) => {
const data = rooms.filter(r => parseInt(r.id) === parseInt(roomId))
return data;
}
const Room = () => {
const roomId = useParams().id;
const [days, setDays] = useState();
const [fullName, setFullName] = useState();
const [amountToPay, setAmountToPay] = useState()
const [roomInfo, setRoomInfo] = useState(getRoomData(roomId));
const calculate = (e) => {
e.preventDefault();
// sadece ekstra bir önlem
if(days>roomInfo[0].availableFor || days<=0){
alert("invalid input for days")
return
}
const result = parseInt(days) * parseInt(roomInfo[0].dailyPrice)
setAmountToPay(result);
}
const makeReservations = () => {
window.confirm("Rezervasyonunuz tamamlandı, keyfini çıkarın")
}
return (<>
<div className='font-bold w-5/6 text-cyan-500 flex flex-col items-center m-auto'>
<h1 className='text-6xl text-center m-4 uppercase'>{roomInfo[0].name}</h1>
<img src={roomInfo[0].src} alt="" />
<p className='text-2xl'>{roomInfo[0].info}</p>
<p className='text-2xl'>Daily Price is: {roomInfo[0].dailyPrice} Money Units</p>
<p className='text-2xl'>For: {roomInfo[0].people}</p>
<p className='text-2xl mb-4'>Available For: {roomInfo[0].availableFor} Days</p>
<div className='mb-10'>
<form onSubmit={calculate} className='bg-purple-50 p-5 '>
<div className='flex flex-col min-w-[300px]'>
<label htmlFor="full-name">Full Name</label>
<input
placeholder='John Doe'
className='p-4'
name='full-name'
type="text"
onChange={(e) => setFullName(e.target.value)}
/>
</div>
<div className='flex flex-col min-w-[300px]'>
<label htmlFor="days">Days</label>
<input
placeholder='1'
className='p-4'
name='days'
type="number"
min={1}
max={roomInfo[0].availableFor}
onChange={(e) => setDays(e.target.value)}
/>
</div>
<input className='w-full mt-4 hover:bg-cyan-600 cursor-pointer rounded p-4 text-white text-2xl bg-cyan-700' type="submit" value="CALCULATE" />
</form>
</div>
<div className='my-6'>
{amountToPay ? <div className='mt-6 flex flex-col items-center'><p className='text-2xl'>Make reservation with the following information:</p><p className='text-xl'>Name: {fullName}</p><p>Amount To Pay: {amountToPay} Money</p><button className='w-full bg-purple-700 text-white p-4 rounded text-2xl mt-2' onClick={makeReservations}>Make Reservation</button></div> : ""}
</div>
</div>
</>)
}
export default Room
Kod çok açıklayıcı ama anlamakta zorlandığınız kısımlar varsa, biraz oynayıp değiştirerek işlevini anlayabileceğinizi düşünüyorum. Projeyi nasıl etkileyeceğini görmek için bazı değerleri değiştirin. Projede geliştirilebilecek pek çok şey bulabileceğinize eminim.
Bu projeyi oluştrmak benim için kesinlikle çok eğlenceliydi. Bir web geliştiricisi olmak istiyorsanız, portföyünüzde bunun gibi web sitelerine ihtiyacınız olacak. Yeni başlayanların anlayabilmesi için bu projenin yeterince basit olmasını istedim. Yine de öğrenilecek kadar karmaşık. react-router-dom ile çalıştık (react-router-dom'dan bir Navlink kullanmamamıza rağmen) ve state değerlerini ayarlayarak dinamik olarak filtreledik. Ayrıca react.js'de formların nasıl ele alınacağını da gördük.
Projeyi geliştirmekten çekinmeyin. Kendi tasarımınızı yapın, daha fazla oda ekleyin, bir backend (arka uç) oluşturun, vb. Her zaman daha iyi hale getirebilirsiniz, her zaman iyileştirme için yer vardır. Umarım bu gönderiden birkaç şey öğrenmişsinizdir ve sizin için tamamen zaman kaybı olmamıştır.
Projenin kodu (ve resimler) burada bulunabilir.
Ilker Akbiyik