React yakuniy loyiha: vazifalar ilovasi

Bu darsda butun bo’lim bo’ylab o’rganilganlarni bitta ilovada birlashtiramiz: vazifalar ro’yxati — qo’shish, bajarilgan deb belgilash, o’chirish, filtrlash va brauzerda saqlash.

JavaScript yakuniy loyihasi darsida xuddi shu ilovani DOM bilan yozgan edingiz. Ikkalasini yonma-yon qo’yish — React nimani o’zgartirganini ko’rishning eng aniq yo’li.

Bo’limni jamlash

Ilovani yozishdan oldin o’tilganni bir ko’rib chiqamiz.

Tushuncha Dars Loyihada qayerda
Deklarativ yondashuv React nima Hisoblagich tasks dan hisoblanadi
Vite loyihasi O’rnatish va Vite Loyiha asosi
JSX qoidalari JSX className, {}, fragment
Komponentga bo’lish Komponentlar To’rtta alohida fayl
Ma’lumot uzatish Props task, tasks, current
Ro’yxat chizish Ro’yxat va key map va key={task.id}
Hodisalar Hodisalar onClick, onChange, onSubmit
Holat useState tasks, filter, title
Forma Forma TaskForm boshqariladigan maydoni
Shartli ko’rinish Shartli ko’rinish Bo’sh ro’yxat xabari
Yon ta’sir useEffect localStorage ga saqlash
Holatni ko’tarish Holatni ko’tarish tasks App da

Ikki dars loyihada bevosita ishlatilmaydi, lekin bilim sifatida qoladi: serverdan ma’lumot yuklash — ma’lumot localStorage o’rniga serverdan kelsa o’sha naqsh ishlaydi.

Yakuniy loyiha

Topshiriq

Vazifalar ilovasini yozing. U quyidagilarni qila olishi kerak:

  1. Forma orqali yangi vazifa qo’shish;
  2. Checkbox bilan bajarilgan deb belgilash va qaytarish;
  3. Vazifani o’chirish;
  4. Filtrlash: hammasi / faol / bajarilgan;
  5. Bajarilmagan vazifalar sonini ko’rsatish;
  6. Ro’yxatni localStorage da saqlash — sahifa yangilansa yo’qolmasin.

Baholash mezoni

Ishingizni shu ro’yxat bo’yicha tekshiring:

Mezon Talab
Komponentlar Kamida to’rtta alohida fayl
Holat joyi tasks faqat App da, title TaskForm da
O’zgarmaslik push, splice, sort ishlatilmagan
key task.id, indeks emas
Forma onSubmit va preventDefault bor
Bo’sh holat Har filtr uchun xabar chiqadi
Hisoblash activeCount holatda saqlanmaydi
Saqlash Sahifa yangilangach ro’yxat joyida qoladi
Konsol Xato va ogohlantirish yo’q

Reja

Loyihani qadam-baqadam quring. Har qadamdan keyin brauzerda tekshiring — oxirida hammasini birdan tuzatishdan ko’ra osonroq.

1-qadam. Loyiha va tuzilma.

npm create vite@latest task-app -- --template react
cd task-app
npm install
npm run dev

src/App.jsx dagi namunaviy tarkibni o’chiring, src/App.css ni bo’shating va src/components/ papkasini yarating.

2-qadam. Ma’lumot shakli. Har vazifa shunday obyekt bo’ladi:

{ id: "a1b2c3", title: "Non olish", isDone: false }

3-qadam. TaskItem — bitta qator: checkbox, matn, o’chirish tugmasi.

4-qadam. TaskListmap bilan ro’yxat, bo’sh holat xabari bilan.

5-qadam. TaskForm — boshqariladigan maydon va onSubmit.

6-qadam. Filter — uchta tugma, joriysi ajralib tursin.

7-qadam. App — holat, uchta ishlovchi, filtrlash va hisoblagich.

8-qadam. useEffect bilan localStorage ga saqlash.

Komponentlar

App                    ← tasks va filter holati
├── TaskForm           ← title holati (o'ziniki)
├── Filter             ← holatsiz
└── TaskList           ← holatsiz
    └── TaskItem       ← holatsiz

Faqat ikki joyda holat bor. Qolgan uchta komponent props oladi va ko’rsatadi — ularni tushunish oson, chunki xotirasi yo’q.

TaskItem

// src/components/TaskItem.jsx
function TaskItem({ task, onToggle, onDelete }) {
  return (
    <li className={task.isDone ? "task done" : "task"}>
      <label>
        <input
          type="checkbox"
          checked={task.isDone}
          onChange={() => onToggle(task.id)}
        />
        <span>{task.title}</span>
      </label>
      <button type="button" onClick={() => onDelete(task.id)}>
        O'chirish
      </button>
    </li>
  );
}

export default TaskItem;

Komponent hech nima hal qilmaydi — onToggle va onDelete ni chaqiradi, xolos. Qaror App da qabul qilinadi.

TaskList

// src/components/TaskList.jsx
import TaskItem from './TaskItem.jsx';

function TaskList({ tasks, onToggle, onDelete }) {
  if (tasks.length === 0) {
    return <p className="empty">Bu bo'limda vazifa yo'q.</p>;
  }

  return (
    <ul className="task-list">
      {tasks.map((task) => (
        <TaskItem
          key={task.id}
          task={task}
          onToggle={onToggle}
          onDelete={onDelete}
        />
      ))}
    </ul>
  );
}

export default TaskList;

TaskForm

// src/components/TaskForm.jsx
import { useState } from 'react';

function TaskForm({ onAdd }) {
  const [title, setTitle] = useState("");

  function handleSubmit(e) {
    e.preventDefault();

    const trimmed = title.trim();
    if (trimmed === "") {
      return;
    }

    onAdd(trimmed);
    setTitle("");
  }

  return (
    <form className="task-form" onSubmit={handleSubmit}>
      <input
        type="text"
        value={title}
        onChange={(e) => setTitle(e.target.value)}
        placeholder="Yangi vazifa"
      />
      <button type="submit">Qo'shish</button>
    </form>
  );
}

export default TaskForm;

title shu yerda qoladi — u boshqa hech kimga kerak emas.

Filter

// src/components/Filter.jsx
const FILTERS = [
  { value: "all", label: "Hammasi" },
  { value: "active", label: "Faol" },
  { value: "done", label: "Bajarilgan" },
];

function Filter({ current, onChange }) {
  return (
    <div className="filter">
      {FILTERS.map((filter) => (
        <button
          key={filter.value}
          type="button"
          className={filter.value === current ? "active" : ""}
          onClick={() => onChange(filter.value)}
        >
          {filter.label}
        </button>
      ))}
    </div>
  );
}

export default Filter;

Tugmalar qo’lda yozilmadi — FILTERS massividan map bilan hosil qilindi. Yangi filtr qo’shish uchun massivga bitta qator qo’shish kifoya.

App

// src/App.jsx
import { useState, useEffect } from 'react';
import TaskForm from './components/TaskForm.jsx';
import TaskList from './components/TaskList.jsx';
import Filter from './components/Filter.jsx';
import './App.css';

const STORAGE_KEY = "learncode-tasks";

function loadTasks() {
  try {
    const saved = localStorage.getItem(STORAGE_KEY);
    return saved ? JSON.parse(saved) : [];
  } catch {
    return [];
  }
}

function App() {
  const [tasks, setTasks] = useState(loadTasks);
  const [filter, setFilter] = useState("all");

  useEffect(() => {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks));
  }, [tasks]);

  function handleAdd(title) {
    const newTask = { id: crypto.randomUUID(), title, isDone: false };
    setTasks([...tasks, newTask]);
  }

  function handleToggle(taskId) {
    setTasks(
      tasks.map((task) =>
        task.id === taskId ? { ...task, isDone: !task.isDone } : task
      )
    );
  }

  function handleDelete(taskId) {
    setTasks(tasks.filter((task) => task.id !== taskId));
  }

  const visibleTasks = tasks.filter((task) => {
    if (filter === "active") return !task.isDone;
    if (filter === "done") return task.isDone;
    return true;
  });

  const activeCount = tasks.filter((task) => !task.isDone).length;

  return (
    <main className="app">
      <h1>Vazifalar</h1>
      <TaskForm onAdd={handleAdd} />
      <Filter current={filter} onChange={setFilter} />
      <p className="counter">Bajarilmagan: {activeCount} ta</p>
      <TaskList
        tasks={visibleTasks}
        onToggle={handleToggle}
        onDelete={handleDelete}
      />
    </main>
  );
}

export default App;

Uslub

src/App.css ga eng zarurini yozing:

.app {
  max-width: 480px;
  margin: 40px auto;
  font-family: system-ui, sans-serif;
}

.task-form {
  display: flex;
  gap: 8px;
  margin-bottom: 16px;
}

.task-form input {
  flex: 1;
  padding: 8px;
}

.filter button.active {
  font-weight: bold;
  text-decoration: underline;
}

.task-list {
  list-style: none;
  padding: 0;
}

.task {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 0;
  border-bottom: 1px solid #ddd;
}

.task.done span {
  text-decoration: line-through;
  color: #888;
}

.empty {
  color: #888;
  text-align: center;
}

Tekshirish

Brauzerda http://localhost:5173 ni oching va ketma-ket bajaring:

  1. Ikkita vazifa qo’shing — ro’yxatda ikkita qator, hisoblagichda 2.
  2. Birinchisini belgilang — matn chizilib o’tadi, hisoblagich 1.
  3. “Faol” bosing — bitta qator qoladi.
  4. “Bajarilgan” bosing — boshqa bitta qator.
  5. “Hammasi” bosing — ikkalasi qaytadi.
  6. Birini o’chiring — bitta qator qoladi.
  7. Sahifani yangilang — ro’yxat joyida turibdi.
  8. Hamma vazifani o’chiring — “vazifa yo’q” xabari chiqadi.
  9. Bo’sh maydon bilan “Qo’shish” bosing — hech nima qo’shilmaydi.
  10. Konsolni oching — xato va ogohlantirish bo’lmasin.

O’ntasi ham o’tsa, loyiha topshirilgan hisoblanadi.

DOM versiyasi bilan taqqoslash

JavaScript yakuniy loyihasi bilan solishtiring:

Vazifa DOM versiyasi React versiyasi
Vazifa qo’shish Massivga qo’shish + createElement + append + hisoblagich setTasks([...tasks, newTask])
Belgilash Massivni yangilash + sinf almashtirish + hisoblagich setTasks(tasks.map(...))
O’chirish Massivdan olib tashlash + remove() + hisoblagich setTasks(tasks.filter(...))
Filtrlash Butun ro’yxatni qayta chizish filter natijasi uzatiladi
Hisoblagich Har amalda qo’lda yangilanadi tasks dan hisoblanadi

Har qatorda bir xil naqsh: DOM versiyasida ikki-uch ish, React’da bitta. Ma’lumot o’zgaradi, ekran o’zi ergashadi.

React nima darsidagi muammo — “hisoblagichni yangilashni unutdim” — React versiyasida umuman yuzaga kelmaydi, chunki yangilanadigan alohida hisoblagich yo’q.

Kengaytirish

Asosiy ilova ishlagach, shularni qo’shib ko’ring:

  1. Tahrirlash — vazifa matniga ikki marta bosilganda maydonga aylansin. editingId holati kerak bo’ladi.
  2. Qidiruvholatni ko’tarish darsidagi SearchInput ni filtr bilan birga ishlating.
  3. Muhimlik darajasiselect qo’shing va ro’yxatni saralang. Saralashda [...tasks].sort(...) — asl massivni o’zgartirmang.
  4. “Bajarilganlarni tozalash” tugmasi — bitta filter chaqiruvi.
  5. Serverga ko’chirishma’lumot yuklash darsidagi naqsh bilan localStorage o’rniga API ishlating.

Bilimni tekshirish

Kodga qaramay javob berishga urining. Qiynalgan savolda tegishli darsga qayting.

  1. onClick={handleDelete(task.id)} deb yozsangiz nima bo’ladi va nega?
  2. setTasks(tasks) chaqirilsa React ekranni yangilaydimi?
  3. Ro’yxat boshiga element qo’shilganda key={index} qanday xatoga olib keladi?
  4. {tasks.length && <p>Jami</p>} — ro’yxat bo’sh bo’lsa ekranda nima ko’rinadi?
  5. useEffect(() => {...}, []) ishlab chiqishda nechta marta ishlaydi va nega?
  6. activeCount uchun useState ishlatish nega yaxshi emas?
  7. TaskForm dagi title holatini nega App ga ko’tarish shart emas?
  8. value berilgan <input> da onChange bo’lmasa nima bo’ladi?
  9. AbortController qaysi muammoni hal qiladi?
  10. Komponent nomi kichik harf bilan yozilsa ekranda nima ko’rinadi?

Javoblar tartib bilan: 1 — funksiya chizish paytida chaqiriladi va tugma ishlamaydi (hodisalar); 2 — yo’q, obyekt o’sha-o’sha (useState); 3 — holat va forma maydonlari aralashib ketadi (ro’yxat va key); 4 — yolg’iz 0 (shartli ko’rinish); 5 — ikki marta, StrictMode tozalashni tekshiradi (useEffect); 6 — ikki holatni mos ushlab turish kerak bo’ladi; 7 — u faqat formaga tegishli (holatni ko’tarish); 8 — maydon qotib qoladi (forma); 9 — javob kelguncha komponent ekrandan ketishi (ma’lumot yuklash); 10 — bo’sh HTML teg, hech nima (komponentlar).

Keyin nima

Bu bo’lim React’ning asosini qamradi: komponent, props, holat, hodisa, effekt. Shu bilim bilan haqiqiy ilovalar yoziladi.

Keyingi qadamlar odatda shunday bo’ladi:

  • Router — bir nechta sahifa va manzil bo’yicha o’tish. Hozir ilovangiz bitta ekrandan iborat; router /tasks va /about kabi yo’llarni qo’shadi.
  • Contextholatni ko’tarish darsida tilga olingan props drilling muammosining yechimi. Mavzu, til va kirgan foydalanuvchi kabi hamma joyda kerak ma’lumot uchun.
  • O’z hook’ingiz — takrorlanadigan mantiqni ajratish. Masalan useLocalStorage yoki useFetch: use bilan boshlanadigan oddiy funksiya ichida boshqa hook’lar chaqiriladi.
  • useReducer — holat o’zgarishlari ko’payib, App dagi ishlovchilar soni o’nlab bo’lganda tartibga soladi.

Har birini alohida o’rganish shart emas. Ilovangiz o’sib, muammo tug’ilganda o’sha muammoning yechimini qidiring — vosita shunda eslab qolinadi.

Hozircha esa ishlaydigan React ilovangiz bor va uni o’zingiz noldan yozdingiz.

Bu dars foydali bo'ldimi?

marta ko'rildi