- 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
useStateiçinde, değiştirmeden güncelleyerek tutmak useEffectile 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.
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" />
</>
);
}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.
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>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.
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} />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.
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>
);
}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
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.
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.
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>
);
}- 1Bir proje oluştur
Terminalde çalıştır:
npm create vite@latest my-app -- --template react - 2Bağımlılıkları kur
cd my-appvenpm install - 3Başlat
npm run dev, ardından tarayıcıdahttp://localhost:5173adresini aç.src/App.jsxdosyasını değiştirdikçe sayfa anında güncellenir.
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.
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 } ]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.
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.
useStatebir 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
mapile gösterilir ve her öğeye kalıcı, benzersiz birkeygerekir; liste değişebiliyorsa indeks değil.
Kendini test et
10 soru. Her doğru cevap XP kazandırır.