İçeriğe geç
Educora
İleri24 dk25 / 27

React temelleri

Arayüzü bileşenlerden kur: JSX, props, `useState` ile durum, `useEffect` ile yan etkiler ve anahtarlı listeler; en popüler UI kütüphanesinin temel fikirleri.

Kendini test et
Bu derste öğreneceklerin
  • Bileşenleri ve JSX'i, ayrıca JSX'in fonksiyon çağrılarına nasıl dönüştüğünü açıklamak
  • Props ile veri aktarmak ve değişen verileri useState içinde, değiştirmeden güncelleyerek tutmak
  • useEffect ile veri yüklemek (bağımlılıklar ve temizlik) ve listeleri kalıcı key'lerle göstermek

DOM dersindeki yapılacaklar listesini hatırla: öğeyi bul, bir li oluştur, ekle, sınıfı değiştir... Uygulama büyüdükçe sayfayı verilerle elle uyumlu tutmak karmaşaya dönüşür. Facebook'ta oluşturulan ve 2013'te açık kaynak yapılan React yaklaşımı tersine çevirir: sen mevcut veriler için arayüzün nasıl görünmesi gerektiğini tarif edersin, DOM'u ise React kendisi günceller. Kısacası: UI = f(durum).

Bileşenler ve JSX

Bir bileşen (component), arayüzün bir tarifini döndüren fonksiyondur; adı büyük harfle başlar. JSX, JavaScript içinde HTML'e benzeyen bir yazımdır; süslü parantez { } içinde herhangi bir JavaScript ifadesi yazılabilir. JSX'te class yerine className, olaylar için de onClick gibi camelCase adlar kullanılır. Bir bileşen tek bir kök öğe (ya da bir <>...</> parçası) döndürür.

JavaScript
function Greeting({ name, city }) {
  const hour = 9;
  return (
    <div className="card">
      <h2>Hello, {name}!</h2>
      <p>{city ? `You are in ${city}` : 'Where are you?'}</p>
      <p>{hour < 12 ? 'Good morning' : 'Good afternoon'}</p>
    </div>
  );
}

export default function App() {
  return (
    <>
      <Greeting name="Aysel" city="Baku" />
      <Greeting name="Murad" />
    </>
  );
}
Ekranda iki kart görünür: “Hello, Aysel! You are in Baku” ve “Hello, Murad! Where are you?”. Tarayıcılar JSX'i doğrudan anlamaz; onu Vite gibi bir derleme aracı düz JavaScript'e çevirir.

JSX yalnızca fonksiyon çağrılarıdır: <h2 className="title">Hi</h2>, öğenin tipini, props'unu ve çocuklarını tarif eden bir nesne oluşturan çağrıya dönüşür. Bunu küçük bir h fonksiyonuyla kendimiz tekrarlayalım; React'in “sanal DOM” dediği şey tam olarak böyle nesnelerden oluşan bir ağaçtır.

JavaScript
function h(type, props, ...children) {
  return { type, props: props ?? {}, children: children.flat() };
}

function render(node) {
  if (typeof node === 'string' || typeof node === 'number') return String(node);
  if (typeof node.type === 'function') return render(node.type(node.props));
  const attrs = Object.entries(node.props)
    .map(([key, value]) => ` ${key === 'className' ? 'class' : key}="${value}"`)
    .join('');
  return `<${node.type}${attrs}>${node.children.map(render).join('')}</${node.type}>`;
}

const Greeting = ({ name }) => h('h2', { className: 'title' }, 'Hello, ', name, '!');

const app = h('div', null, h(Greeting, { name: 'Aysel' }), h('p', null, 'Items: ', 3));
console.log(JSON.stringify(app.children[1]));
console.log(render(app));
▸ Beklenen çıktı
{"type":"p","props":{},"children":["Items: ",3]}
<div><h2 class="title">Hello, Aysel!</h2><p>Items: 3</p></div>
React bu ağacı bir öncekiyle karşılaştırır ve DOM'da yalnızca farkları değiştirir; buna uzlaştırma (reconciliation) denir.

Props

Props, bir bileşenin girdileridir ve HTML nitelikleri gibi verilir: string'ler tırnak içinde, geri kalan her şey (sayılar, diziler, nesneler, fonksiyonlar) süslü parantez içinde. Props salt okunurdur: bileşen onları değiştirmemelidir. Veriler yukarıdan aşağıya (ebeveyn → çocuk) akar, olaylar ise props olarak verilen callback'ler aracılığıyla yukarı çıkar. Etiketler arasındaki içerik children prop'una düşer.

JavaScript
function ProductCard({ title, price, inStock = true, onBuy }) {
  return (
    <article className="product">
      <h3>{title}</h3>
      <p>{price.toFixed(2)} AZN</p>
      {inStock ? (
        <button onClick={() => onBuy(title)}>Buy</button>
      ) : (
        <span>Out of stock</span>
      )}
    </article>
  );
}

// inside a parent component:
<ProductCard title="Notebook" price={4.5} onBuy={(name) => console.log('Buying', name)} />
<ProductCard title="Pen" price={1} inStock={false} />
İlk kartta bir “Buy” düğmesi var; ona basınca konsolda Buying Notebook görünür. İkinci kartta “Out of stock” yazar.

useState ile durum

Durum (state), zamanla değişen ve arayüzü güncellemesi gereken verilerdir: bir sayaç, yazılan metin, bir görev listesi. const [value, setValue] = useState(başlangıç) sana mevcut değeri ve onu değiştiren fonksiyonu verir. setValue çağrıldığında React bileşen fonksiyonunu yeni değerle yeniden çağırır (yeniden render). count = count + 1 gibi doğrudan bir atama ekranı güncellemez. Hook'lar yalnızca bileşenin en üst düzeyinde çağrılır; bir if ya da döngü içinde asla.

JavaScript
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <button onClick={() => setCount((c) => c + 3)}>+3</button>
      <button onClick={() => setCount(0)}>Reset</button>
    </div>
  );
}
JavaScript
let todos = [
  { id: 1, text: 'Learn JSX', done: true },
  { id: 2, text: 'Learn useState', done: false },
];

const addTodo = (list, text) => [...list, { id: list.length + 1, text, done: false }];
const toggleTodo = (list, id) =>
  list.map((todo) => (todo.id === id ? { ...todo, done: !todo.done } : todo));
const removeTodo = (list, id) => list.filter((todo) => todo.id !== id);

const before = todos;
todos = addTodo(todos, 'Learn useEffect');
todos = toggleTodo(todos, 2);
todos = removeTodo(todos, 1);

console.log(todos.map((t) => `${t.done ? '[x]' : '[ ]'} ${t.text}`));
console.log(before === todos, before.length);
▸ Beklenen çıktı
[ '[x] Learn useState', '[ ] Learn useEffect' ]
false 2
Bir React bileşeninde bu fonksiyonlar setTodos((list) => addTodo(list, text)) gibi çağrılır. Eski dizi el değmeden kalır; React yeni referansı görüp ekranı günceller.

useEffect ile etkiler

Render saf olmalıdır: yalnızca props ve durumdan arayüzü hesaplar. Dış dünyaya dokunan işler (veri yüklemek, zamanlayıcılar, abonelikler) yan etkilerdir ve useEffect(fn, bağımlılıklar) içine yazılır. React etkiyi, render ekrana yansıdıktan sonra çalıştırır. Bağımlılık dizisi zamanlamayı yönetir: [] ilk render'dan sonra bir kez; [userId] userId her değiştiğinde; dizi yoksa her render'dan sonra. Etkiden döndürülen fonksiyon temizliktir: bileşen kaybolduğunda ya da etki yeniden çalışmadan önce zamanlayıcıları ve istekleri iptal eder.

JavaScript
import { useEffect, useState } from 'react';

function UserCard({ userId }) {
  const [user, setUser] = useState(null);
  const [error, setError] = useState('');

  useEffect(() => {
    const controller = new AbortController();
    setUser(null);
    fetch(`https://api.example.com/users/${userId}`, { signal: controller.signal })
      .then((res) => {
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        return res.json();
      })
      .then(setUser)
      .catch((err) => {
        if (err.name !== 'AbortError') setError(err.message);
      });
    return () => controller.abort();
  }, [userId]);

  if (error) return <p>Error: {error}</p>;
  if (!user) return <p>Loading...</p>;
  return <h2>{user.name}</h2>;
}
userId değişince temizlik eski isteği iptal eder; eski kullanıcı için geç gelen bir yanıt yenisinin üzerine yazılamaz.

Listeler ve anahtarlar

Bir diziyi map ile gösterirsin: her eleman için JSX döndürülür ve her birine bir key gerekir; kardeşleri arasında benzersiz ve kalıcı bir kimlik (genellikle verideki id). Anahtarlar sayesinde React öğeleri render'lar arasında tanır: bir şey eklendiğinde, silindiğinde ya da yer değiştirdiğinde hangi DOM düğümünün hangi öğeye ait olduğunu bilir. Liste değişebiliyorsa indeksi anahtar olarak kullanma.

JavaScript
function TodoList({ todos, onToggle }) {
  if (todos.length === 0) return <p>Nothing to do!</p>;

  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id} className={todo.done ? 'done' : ''} onClick={() => onToggle(todo.id)}>
          {todo.text}
        </li>
      ))}
    </ul>
  );
}
  1. 1
    Bir proje oluştur

    Terminalde çalıştır: npm create vite@latest my-app -- --template react

  2. 2
    Bağımlılıkları kur

    cd my-app ve npm install

  3. 3
    Başlat

    npm run dev, ardından tarayıcıda http://localhost:5173 adresini aç. src/App.jsx dosyasını değiştirdikçe sayfa anında güncellenir.

Alıştırma

useReducer hook'u tam olarak böyle reducer'larla çalışır. cartReducer(cart, action) fonksiyonunu tamamla: add yeni bir ürünü qty: 1 ile ekler, increment bir ürünün adedini 1 artırır, remove ürünü siler. Hiçbir diziyi ya da nesneyi değiştirme; her zaman yenisini döndür.

Alıştırma · JavaScript
function cartReducer(cart, action) {
  switch (action.type) {
    case 'add':
      // return a new array with { id: action.id, qty: 1 } at the end
    case 'increment':
      // return a new array where that item has qty + 1
    case 'remove':
      // return a new array without that item
    default:
      return cart;
  }
}

const start = [{ id: 'tea', qty: 1 }];
const actions = [
  { type: 'add', id: 'bread' },
  { type: 'increment', id: 'tea' },
  { type: 'increment', id: 'tea' },
  { type: 'remove', id: 'bread' },
];

console.log(actions.reduce(cartReducer, start));
console.log(start);
▸ Beklenen çıktı
[ { id: 'tea', qty: 3 } ]
[ { id: 'tea', qty: 1 } ]
Alıştırma

Dersteki h ve render fonksiyonları verildi. h ile bir TodoList({ todos }) bileşeni yaz: her görev için bir li içeren bir ul döndürsün, biten görevin li'si ise className: 'done' alsın.

Alıştırma · JavaScript
function h(type, props, ...children) {
  return { type, props: props ?? {}, children: children.flat() };
}

function render(node) {
  if (typeof node === 'string' || typeof node === 'number') return String(node);
  if (typeof node.type === 'function') return render(node.type(node.props));
  const attrs = Object.entries(node.props)
    .map(([key, value]) => ` ${key === 'className' ? 'class' : key}="${value}"`)
    .join('');
  return `<${node.type}${attrs}>${node.children.map(render).join('')}</${node.type}>`;
}

// map todos to li elements inside the ul
const TodoList = ({ todos }) => h('ul', null);

const todos = [
  { id: 1, text: 'Learn JSX', done: true },
  { id: 2, text: 'Learn hooks', done: false },
];
console.log(render(h(TodoList, { todos })));
▸ Beklenen çıktı
<ul><li class="done">Learn JSX</li><li>Learn hooks</li></ul>

Önemli noktalar

  • Bileşen, props'tan arayüz döndüren bir fonksiyondur; JSX, öğeleri tarif eden fonksiyon çağrılarına derlenir.
  • Props yukarıdan aşağı akar ve salt okunurdur; olaylar props olarak verilen callback'lerle yukarı çıkar.
  • useState bir değer ve bir güncelleyici verir; güncelleyiciyi çağırmak yeniden render eder; dizileri ve nesneleri değiştirmeden güncelle.
  • useEffect, render sonrasındaki yan etkiler içindir: bağımlılık dizisi zamanlamayı seçer, döndürülen fonksiyon temizlik yapar.
  • Listeler map ile gösterilir ve her öğeye kalıcı, benzersiz bir key gerekir; liste değişebiliyorsa indeks değil.

Kendini test et

10 soru. Her doğru cevap XP kazandırır.

1 / 10
Derleme adımından sonra JSX neye dönüşür?