Məzmuna keç
Educora
İrəli24 dəq25 / 27

React əsasları

İnterfeysi komponentlərdən qur: JSX, props, `useState` ilə vəziyyət, `useEffect` ilə yan təsirlər və açarlı siyahılar — ən populyar UI kitabxanasının əsas ideyaları.

Özünü yoxla
Bu dərsdə öyrənəcəksən
  • 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 useState ilə, dəyişmədən yeniləyərək saxlamaq
  • useEffect ilə verilən yükləmək (asılılıqlar və təmizləmə) və siyahıları sabit key-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.

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» və «Hello, Murad! Where are you?». JSX-i brauzer birbaşa başa düşmür — onu Vite kimi qurma aləti adi JavaScript-ə çevirir.

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.

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));
▸ Gözlənilən nəticə
{"type":"p","props":{},"children":["Items: ",3]}
<div><h2 class="title">Hello, Aysel!</h2><p>Items: 3</p></div>
React bu ağacı əvvəlki ilə müqayisə edir və DOM-da yalnız fərqləri dəyişir — buna uzlaşdırma (reconciliation) deyilir.

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.

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} />
Birinci kartda «Buy» düyməsi var — ona basanda konsolda 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.

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);
▸ Gözlənilən nəticə
[ '[x] Learn useState', '[ ] Learn useEffect' ]
false 2
React komponentində bu funksiyalar 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.

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 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ə.

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
    Layihə yarat

    Terminalda icra et: npm create vite@latest my-app -- --template react

  2. 2
    Asılılıqları quraşdır

    cd my-app və npm install

  3. 3
    İşə sal

    npm run dev və brauzerdə http://localhost:5173 ünvanını aç. src/App.jsx faylını dəyişdikcə səhifə dərhal yenilənir.

Tapşırıq

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.

Tapşırıq · 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);
▸ Gözlənilən nəticə
[ { id: 'tea', qty: 3 } ]
[ { id: 'tea', qty: 1 } ]
Tapşırıq

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.

Tapşırıq · 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 })));
▸ 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.
  • useState qiymə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ə.
  • useEffect renderdən sonrakı yan təsirlər üçündür: asılılıqlar massivi vaxtı seçir, qaytarılan funksiya təmizləyir.
  • Siyahılar map ilə göstərilir və hər elementə sabit, unikal key lazımdır — siyahı dəyişə bilərsə, indeks yox.

Özünü yoxla

10 sual. Hər düzgün cavab XP qazandırır.

1 / 10
Qurma mərhələsindən sonra JSX nəyə çevrilir?