- Komponentləri və JSX-i, həmçinin JSX-in funksiya çağırışlarına necə çevrildiyini izah etmək
- Props ilə verilən ötürmək və dəyişən verilənləri
useStateilə, dəyişmədən yeniləyərək saxlamaq useEffectilə verilən yükləmək (asılılıqlar və təmizləmə) və siyahıları sabitkey-lərlə göstərmək
DOM dərsindəki tapşırıqlar siyahısını xatırla: elementi tap, li yarat, əlavə et, sinfi dəyiş... Proqram böyüdükcə səhifəni verilənlərlə əl ilə sinxron saxlamaq qarmaqarışıqlığa çevrilir. Facebook-da yaradılan və 2013-cü ildə açıq mənbəli edilən React yanaşmanı tərsinə çevirir: sən cari verilənlər üçün interfeysin necə görünməli olduğunu təsvir edirsən, DOM-u isə React özü yeniləyir. Qısaca: UI = f(vəziyyət).
Komponentlər və JSX
Komponent interfeysin təsvirini qaytaran funksiyadır; adı böyük hərflə başlayır. JSX JavaScript-in içində HTML-ə bənzəyən yazılışdır, fiqurlu mötərizədə { } isə istənilən JavaScript ifadəsi yazmaq olar. JSX-də class əvəzinə className, hadisələr üçün onClick kimi camelCase adlar işlədilir. Komponent bir kök element (və ya <>...</> fraqmenti) qaytarı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 sadəcə funksiya çağırışlarıdır: <h2 className="title">Hi</h2> elementin növünü, props-u və uşaqlarını təsvir edən obyekt yaradan çağırışa çevrilir. Bunu kiçik h funksiyası ilə özümüz təkrarlayaq — React-in «virtual DOM» dediyi də məhz belə obyektlər ağacıdı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));▸ Gözlənilən nəticə
{"type":"p","props":{},"children":["Items: ",3]}
<div><h2 class="title">Hello, Aysel!</h2><p>Items: 3</p></div>Props
Props komponentin giriş verilənləridir və HTML atributları kimi ötürülür: sətirlər dırnaqda, qalan hər şey — ədədlər, massivlər, obyektlər, funksiyalar — fiqurlu mötərizədə. Props yalnız oxunur: komponent onları dəyişməməlidir. Verilənlər yuxarıdan aşağı axır (valideyn → uşaq), hadisələr isə props kimi ötürülən callback-lər vasitəsilə yuxarı qalxır. Teqlər arasındakı məzmun children propuna düşür.
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 kartda «Out of stock» yazılıb.useState ilə vəziyyət
Vəziyyət (state) zamanla dəyişən və interfeysi yeniləməli olan verilənlərdir: sayğac, daxil edilən mətn, tapşırıqlar siyahısı. const [value, setValue] = useState(başlanğıc) cari qiyməti və onu dəyişən funksiyanı verir. setValue çağırılanda React komponent funksiyasını yeni qiymətlə yenidən çağırır (yenidən render). count = count + 1 kimi birbaşa mənimsətmə ekranı yeniləmir. Hook-lar yalnız komponentin ən üst səviyyəsində çağırılır — if-in və ya dövrün içində yox.
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);▸ Gözlənilən nəticə
[ '[x] Learn useState', '[ ] Learn useEffect' ] false 2
setTodos((list) => addTodo(list, text)) kimi çağırılır. Köhnə massiv toxunulmaz qalıb — React yeni istinadı görüb ekranı yeniləyir.useEffect ilə effektlər
Render təmiz olmalıdır: props və vəziyyətdən sadəcə interfeysi hesablayır. Kənar dünyaya toxunan işlər — verilən yükləmək, taymerlər, abunəliklər — yan təsirlərdir və useEffect(fn, asılılıqlar) içinə yazılır. React effekti render ekrana çıxandan sonra işə salır. Asılılıqlar massivi vaxtı idarə edir: [] — ilk renderdən sonra bir dəfə; [userId] — userId hər dəyişəndə; massiv yoxdursa — hər renderdən sonra. Effektdən qaytarılan funksiya təmizləmədir: komponent yox olanda və ya effekt yenidən işləməzdən əvvəl taymerləri və sorğuları ləğv edir.
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 dəyişəndə təmizləmə köhnə sorğunu ləğv edir — köhnə istifadəçinin gec gələn cavabı yenisinin yerinə yazılmır.Siyahılar və açarlar
Massivi map ilə göstərirsən: hər element üçün JSX qaytarılır və hər birinə key lazımdır — qonşular arasında unikal və sabit identifikator (adətən verilənlərdəki id). Açarlar sayəsində React elementləri renderlər arasında tanıyır: nəsə əlavə olunanda, silinəndə və ya yeri dəyişəndə hansı DOM düyününün hansı elementə aid olduğunu bilir. Siyahı dəyişə bilərsə, indeksi açar kimi işlətmə.
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>
);
}- 1Layihə yarat
Terminalda icra et:
npm create vite@latest my-app -- --template react - 2Asılılıqları quraşdır
cd my-appvənpm install - 3İşə sal
npm run devvə brauzerdəhttp://localhost:5173ünvanını aç.src/App.jsxfaylını dəyişdikcə səhifə dərhal yenilənir.
useReducer hook-u məhz belə reduktorlarla işləyir. cartReducer(cart, action) funksiyasını tamamla: add yeni məhsulu qty: 1 ilə əlavə edir, increment məhsulun sayını 1 artırır, remove məhsulu silir. Heç bir massivi və obyekti dəyişmə — həmişə yenisini qaytar.
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);▸ Gözlənilən nəticə
[ { id: 'tea', qty: 3 } ]
[ { id: 'tea', qty: 1 } ]Dərsdəki h və render funksiyaları verilib. h ilə TodoList({ todos }) komponentini yaz: o, hər tapşırıq üçün bir li olan ul qaytarsın, bitmiş tapşırıqların li-si isə 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 })));▸ Gözlənilən nəticə
<ul><li class="done">Learn JSX</li><li>Learn hooks</li></ul>
Əsas fikirlər
- Komponent props-dan interfeys qaytaran funksiyadır; JSX elementləri təsvir edən funksiya çağırışlarına çevrilir.
- Props yuxarıdan aşağı axır və yalnız oxunur; hadisələr props kimi ötürülən callback-lərlə yuxarı qalxır.
useStateqiymət və onu dəyişən funksiya verir; funksiyanın çağırılması yenidən render edir; massiv və obyektləri dəyişmədən yenilə.useEffectrenderdən sonrakı yan təsirlər üçündür: asılılıqlar massivi vaxtı seçir, qaytarılan funksiya təmizləyir.- Siyahılar
mapilə göstərilir və hər elementə sabit, unikalkeylazımdır — siyahı dəyişə bilərsə, indeks yox.
Özünü yoxla
10 sual. Hər düzgün cavab XP qazandırır.