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:
- Forma orqali yangi vazifa qo’shish;
- Checkbox bilan bajarilgan deb belgilash va qaytarish;
- Vazifani o’chirish;
- Filtrlash: hammasi / faol / bajarilgan;
- Bajarilmagan vazifalar sonini ko’rsatish;
- Ro’yxatni
localStorageda 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. TaskList — map 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:
- Ikkita vazifa qo’shing — ro’yxatda ikkita qator, hisoblagichda
2. - Birinchisini belgilang — matn chizilib o’tadi, hisoblagich
1. - “Faol” bosing — bitta qator qoladi.
- “Bajarilgan” bosing — boshqa bitta qator.
- “Hammasi” bosing — ikkalasi qaytadi.
- Birini o’chiring — bitta qator qoladi.
- Sahifani yangilang — ro’yxat joyida turibdi.
- Hamma vazifani o’chiring — “vazifa yo’q” xabari chiqadi.
- Bo’sh maydon bilan “Qo’shish” bosing — hech nima qo’shilmaydi.
- 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:
- Tahrirlash — vazifa matniga ikki marta bosilganda maydonga
aylansin.
editingIdholati kerak bo’ladi. - Qidiruv — holatni ko’tarish darsidagi
SearchInputni filtr bilan birga ishlating. - Muhimlik darajasi —
selectqo’shing va ro’yxatni saralang. Saralashda[...tasks].sort(...)— asl massivni o’zgartirmang. - “Bajarilganlarni tozalash” tugmasi — bitta
filterchaqiruvi. - Serverga ko’chirish —
ma’lumot yuklash darsidagi naqsh bilan
localStorageo’rniga API ishlating.
Bilimni tekshirish
Kodga qaramay javob berishga urining. Qiynalgan savolda tegishli darsga qayting.
onClick={handleDelete(task.id)}deb yozsangiz nima bo’ladi va nega?setTasks(tasks)chaqirilsa React ekranni yangilaydimi?- Ro’yxat boshiga element qo’shilganda
key={index}qanday xatoga olib keladi? {tasks.length && <p>Jami</p>}— ro’yxat bo’sh bo’lsa ekranda nima ko’rinadi?useEffect(() => {...}, [])ishlab chiqishda nechta marta ishlaydi va nega?activeCountuchunuseStateishlatish nega yaxshi emas?TaskFormdagititleholatini negaAppga ko’tarish shart emas?valueberilgan<input>daonChangebo’lmasa nima bo’ladi?AbortControllerqaysi muammoni hal qiladi?- 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
/tasksva/aboutkabi yo’llarni qo’shadi. - Context — holatni 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
useLocalStorageyokiuseFetch:usebilan boshlanadigan oddiy funksiya ichida boshqa hook’lar chaqiriladi. useReducer— holat o’zgarishlari ko’payib,Appdagi 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.