nobel-pen

Kurulum:

Vite Kurulumu

Vite ile projemizi kuracağız. Bunun için aşağıdaki komutu yazarız

npm create vite@latest

Bunu yazdıktan sonra bize projenin adının ne olacağı sorulur terminalde; projemizin adı "nobel-pen".

Sonra hangi ortamda kuracağımızı yazarız: React projesi ve JavaScript+SWC'dir.

cd nobel-pen
  npm install
  npm run dev

Projemizin içine gireriz, npm'i kurar ve çalıştırırız.

ilgili github reposu: https://github.com/sanscodex/nobel-pen/tree/c2120b98e2413d3d4a90f4b5aa1e70c0eb485d96

Tailwind CSS Kurulumu

Projemizi oluşturduğumuz için Tailwind kurulumunda sadece bağımlılıkları yükleriz.

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

tailwindcss ve bağımlılıklarını yükleyin, ardından tailwind.config.js ve postcss.config.js dosyalarınızı oluşturun.

tailwind.config.js dosyanızda tüm şablon dosyalarınızın yollarını ekleyin.

/** @type {import('tailwindcss').Config} */
export default {
  content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
};

Her bir Tailwind katmanı için @tailwind direktiflerini ./src/index.css dosyanıza ekleyin.

@tailwind base;
@tailwind components;
@tailwind utilities;

ilgili github repo linki: https://github.com/sanscodex/nobel-pen/tree/4253ba50493200aefeb239aca138e7dca7b3c47a

Mock Data

Nobel edebiyat ödülü alan yazarların olduğu bir mock data ekledik, yazarlardan birini örnek olarak gösterelim.

{
    id: "241248",
    name: "Orhan Pamuk",
    year: "2006",
    imageUrl:
      "https://commons.wikimedia.org/wiki/Special:FilePath/Orhan%20Pamuk%202009%20Shankbone.jpg",
    wiki: "https://en.wikipedia.org/wiki/Orhan_Pamuk",
    countries: "Turkey",
    notableWorks:
      "The Museum of Innocence, The New Life, The Black Book, My Name Is Red, Istanbul: Memories and the City, Cevdet Bey and His Sons, Snow, The White Castle, Nights of Plague",
  },

ilgili github repo linki: https://github.com/sanscodex/nobel-pen/tree/c43517945c47d6dc1c4121458e605015250eab1c

Projenin İskeletini Hazırlamak

İlk olarak src altında component'lerimizi oluşturacağız; ilk component'imiz Header'dır.

import Logo from "../assets/nobel-prize-logo.png";
const Header = () => {
  return (
    <div className="flex justify-around items-center mt-16">
      <img src={Logo} alt="logo" className="w-32"></img>
      <h1 className="text-center text-2xl w-40 text-yellow-300">
        <span className="text-6xl">Nobel</span>{" "}
        <span className="text-5xl">Prizes</span> in Literature
      </h1>
    </div>
  );
};

export default Header;

Burada logomuzun ve başlığımızın olduğu bir header yazıyoruz. Logo'yu src altındaki assets'lere koyuyorum ve çağırırken import ile çağırıp resim uzantısını da yazarız. Bu Header Component'i App.jsx içine göndeririz.

Search yapacağımız component'i de yazıp App.jsx'e import ediyoruz.

const Search = () => {
  return (
    <div className="flex justify-center mt-16">
      <input
        type="text"
        placeholder="Search Author"
        className="w-96 p-4 bg-yellow-400 text-black"
      />
    </div>
  );
};

export default Search;

Ödül alan yazarları data'dan çekeceğimiz Author.jsx'i yazmaya başlayalım.

data named export olduğundan dolayı isim değiştirerek import yapamayız. Ne şekilde yazıldıysa o şekilde import yapabiliriz.

import { data } from "../helpers/data";

const Authors = () => {
  console.log(data);
  return (
    <div className="authors-container">
      <div className="author-card">
        <div className="author-image">
          <img src="#" alt="author-name" />
        </div>
        <div className="author-info">
          <p>Author Name</p>
          <p>Notable Books</p>
          <p>Nobel's year</p>
          <p>Author's Country</p>
          <button>Read More in Wiki</button>
        </div>
      </div>
    </div>
  );
};

export default Authors;

İlk etapta yazarları bu şekilde card'lar ile gösteriyorum. Ayrıca console'da aşağıdaki gibi tüm data'mızı görebiliriz.

console-log-for-data

Projenin Logik Kısımları

Biraz Tailwind ile stil verelim. Yazarları map'liyorum, her gelen veri için yeni bir kart oluşturuyorum ve parametre olarak gelen verileri kartlarda ilgili yerlere yerleştiriyorum. Ve ternary operatör ile useState kullanalım. Resim kartlarına tıklandığında yazara ait bilgiler gözüksün.

import { data } from "../helpers/data";
import { useState } from "react";
const Authors = () => {
  console.log(data);
  const [show, setShow] = useState(false);
  return (
    <div className="authors-container flex flex-row flex-wrap justify-center items-center gap-16 m-8">
      {data.map((authorItem) => {
        return (
          <div
            onClick={() => {
              setShow(!show);
            }}
            key={authorItem.id}
            className="author-card flex border w-80 border-yellow-400 p-4 gap-4"
          >
            {!show ? (
              <div className="author-image w-72  overflow-hidden  bg-slate-400 ">
                <img
                  className="object-cover h-72 w-full"
                  src={authorItem.imageUrl}
                  alt={authorItem.name}
                />
                <a className="border border-yellow-400 p-2 block text-yellow-400 font-bold text-xl  text-center">
                  {authorItem.name}
                </a>
              </div>
            ) : (
              <div className="author-info flex flex-col  w-full  gap-8">
                <p>{authorItem.name}</p>
                <p>{authorItem.notableWorks}</p>
                <p>{authorItem.year}</p>
                <p>{authorItem.countries}</p>
                <a
                  href={authorItem.wiki}
                  className="p-2 w-32 bg-yellow-400  text-slate-800"
                >
                  Read More
                </a>
              </div>
            )}
          </div>
        );
      })}
    </div>
  );
};

export default Authors;

Burada ternary'de dedik ki state false ise not operatörü ile true'ya çevir ve resmi ekrana bas. Değilse yazar bilgilerini ekrana bas. İlk state'in başlangıç değeri false olduğu için ekrana image'ler gelir. Map içerisindeki her bir eleman için bunu yapmış olduk. Card'a tıklandığında da onClick fonksiyonu devreye giriyor, setShow state'i olan show durumunun tersi olacak şekilde güncelliyor.

İlgili GitHub reposu: https://github.com/sanscodex/nobel-pen/tree/0f0c71c552fb3049c7b8e1d02ab5a2e3c5a473e1

Şimdi burada card'ı tıklayınca bütün card'lar değişiyor. Bizim sadece ilgili tıklanan kartı değiştirmemiz, bilgilerini göstermemiz lazım. Buradaki problem değişikliği takip eden state'in 1 tane olması; yani biri değiştiğinde tüm Card'ların değişmesi. Ama benim 122 tane farklı card'ım var. Yani her bir card için ayrı bir state'e ihtiyacımız var. Yazar bilgilerinin görüldüğü yeri farklı bir component yapalım ve orada useState kullanalım. Böylelikle her biri için ayrı tıklanma durumu olur. Virtual DOM farkı kavrayıp sadece değişiklik olan component'i günceller. Dolayısıyla ayrı bir component oluşturuyoruz, her bir author için.

import { useState } from "react";
const Author = ({ authorItem }) => {
  const [show, setShow] = useState(false);
  return (
    <>
      <div
        key={authorItem.id}
        onClick={() => {
          setShow(!show);
        }}
        className="author-card flex border w-80 border-yellow-400 p-4 gap-4"
      >
        {!show ? (
          <div className="author-image w-72  overflow-hidden  bg-slate-400 ">
            <img
              className="object-cover h-72 w-full"
              src={authorItem.imageUrl}
              alt={authorItem.name}
            />
            <a className="border border-yellow-400 p-2 block text-yellow-400 font-bold text-xl  text-center">
              {authorItem.name}
            </a>
          </div>
        ) : (
          <div className="author-info flex flex-col  w-full  gap-8">
            <p>{authorItem.name}</p>
            <p>{authorItem.notableWorks}</p>
            <p>{authorItem.year}</p>
            <p>{authorItem.countries}</p>
            <a
              href={authorItem.wiki}
              className="p-2 w-32 bg-yellow-400  text-slate-800"
            >
              Read More
            </a>
          </div>
        )}
      </div>
    </>
  );
};

export default Author;

Burada tek bir yazara useState kullanıyoruz. Böylelikle tıkladığımız card açılıp yazar bilgileri gözükmüş oluyor.

import { data } from "../helpers/data";
import { useState } from "react";
import Author from "./Author";

const Authors = () => {
  console.log(data);

  return (
    <div className="authors-container flex flex-row flex-wrap justify-center items-center gap-16 m-8">
      {data.map((authorItem) => (
        <Author key={authorItem.id} authorItem={authorItem} />
      ))}
    </div>
  );
};

export default Authors;

Burada Author Card component'ini map'leyerek her birini ayrı ayrı yazdırmış oluruz.

İlgili GitHub reposu: https://github.com/sanscodex/nobel-pen/tree/fd3af82559f85ea42d889569bd720c0f53fce6d8

Şimdi search butonuna filter uygulamam lazım.

Dosya hiyerarşimiz: App.jsx'de hepsini ayrı ayrı çağırdık, dolayısıyla yukarıda App.jsx'te useState tanımlayacağım. Burada handleChange değişimini yapacağım. Bu değişiklikleri aşağı props olarak göndereceğim.

import Authors from "./components/Authors";
import Header from "./components/Header";
import Search from "./components/Search";
import { useState } from "react";

export default function App() {
  const [search, setSearch] = useState("");

  const handleChange = (e) => {
    setSearch(e.target.value);
  };

  return (
    <div>
      <Header />
      <Search handleChange={handleChange} />
      <Authors search={search} />
    </div>
  );
}
import Authors from "./components/Authors";
import Header from "./components/Header";
import Search from "./components/Search";
import { useState } from "react";

Bu satırlar, gerekli bileşenleri ve React'in useState hook'unu import ediyor.

export default function App() {

Bu, Ana App component’ini tanımlıyor.

const [search, setSearch] = useState("");

Burada, arama terimi için bir state oluşturuluyor. Başlangıç değeri boş bir string.

const handleChange = (e) => {
  setSearch(e.target.value);
};

Bu fonksiyon, arama input'undaki değişiklikleri yakalayıp search state'ini güncelliyor.

return (
  <div>
    <Header />
    <Search handleChange={handleChange} >
    <Authors search={search} >
  </div>
);

Bu kısım, bileşenin render edilecek yapısını tanımlıyor. Header, Search ve Authors bileşenlerini içeriyor. Search bileşenine handleChange fonksiyonu, Authors bileşenine ise search state'i prop olarak geçiriliyor.

Bu yapı, arama işlevselliğini üst seviyede (App bileşeninde) yönetip, alt bileşenlere gerekli verileri ve fonksiyonları prop olarak geçiriyor. Bu, React'in "yukarıdan aşağıya veri akışı" prensibine uygun bir yaklaşımdır.

import { useState } from "react";

const Search = (props) => {
  return (
    <div className="flex justify-center mt-16">
      <input
        type="text"
        placeholder="Search Author"
        className="w-96 p-4 bg-yellow-400 text-black"
        onChange={props.handleChange}
      />
    </div>
  );
};

export default Search;

Bu kod parçası, React kullanılarak oluşturulmuş bir arama bileşenini (Search component) tanımlıyor.

const Search = (props) => { ... }

Search adında bir fonksiyon komponenti tanımlıyor. Bu komponent, props adında bir parametre alıyor.

  • Komponent, bir <div> elementi döndürüyor:
  • <div> içinde bir <input/> elementi var:
    • onChange={props.handleChange} - Input'un değeri değiştiğinde çağrılacak fonksiyonu belirliyor. Bu fonksiyon, üst komponentten props aracılığıyla geçiriliyor.
  • export default Search; - Search komponentini dışa aktarıyor, böylece başka dosyalarda import edilebilir.

Bu komponent, kullanıcının yazar araması yapabileceği bir input alanı oluşturuyor. Kullanıcı bu alana bir şey yazdığında, üst komponentten gelen handleChange fonksiyonu çağrılacak ve arama işlemi gerçekleştirilecek.

import { data } from "../helpers/data";
import Author from "./Author";

const Authors = (props) => {
 
  console.log(data);
  const filteredData = data.filter((item) =>
    item.name.toLowerCase().includes(props.search.toLowerCase())
  );
  return (
    <div className="authors-container flex flex-row flex-wrap justify-center items-center gap-16 m-8">
      {filteredData.map((authorItem, index) => (
        <Author key={index} authorItem={authorItem} />
      ))}
    </div>
  );
};

export default Authors;
  • İlk olarak, gerekli modüller ve bileşenler import ediliyor:
import { data } from "../helpers/data";
import Author from "./Author";
  • Authors adında bir fonksiyon komponenti tanımlanıyor:
const Authors = (props) => {
  // ...
};
  • filteredData adında bir değişken oluşturuluyor. Bu değişken, data dizisini filtreler:
const filteredData = data.filter((item) =>
  item.name.toLowerCase().includes(props.search.toLowerCase())
);

Bu filtreleme işlemi, props.search değerini (kullanıcının girdiği arama terimi) içeren yazar isimlerini seçer. Bu yukaridan App.jsx’ten handleChange’te Search.jsx’te her degisiklik oldugunda useState’teki search’i degistirir ve Authors.jsx props olarak gönderir. Büyük/küçük harf duyarlılığını ortadan kaldırmak için toLowerCase() kullanılmıştır.

  • filteredData dizisi map() fonksiyonu ile döngüye alınır:
{filteredData.map((authorItem, index) => (
  <Author key={index} authorItem={authorItem} />
))}

Bu döngü, filtrelenmiş her yazar için bir Author komponenti oluşturur. Her bir Author komponentine, authorItem prop'u olarak ilgili yazar verisi geçirilir. key prop'u olarak index kullanılmıştır.

  • Son olarak, Authors komponenti export edilir:
export default Authors;

Bu kod, arama işlevselliği olan bir yazar listesi oluşturur. Kullanıcı bir arama terimi girdiğinde, liste dinamik olarak filtrelenir ve sadece arama terimiyle eşleşen yazarları gösterir.

ilgili github linki: https://github.com/sanscodex/nobel-pen/tree/abb5a450238340fc25408486abaeb861196f274b

Bu projede kullanılan React özellikleri : Vite ile proje kurulumu, fonksiyon bileşenleri, useState hook'u, prop'lar, map fonksiyonu ile liste render etme ve koşullu render etme (ternary operatör kullanarak).

https://github.com/sanscodex/nobel-pen

— Github Repo

https://nobelpen.netlify.app

— Demo
Share:

Yorum yap