React: holatni ko'tarish

Serverdan ma’lumot yuklash darsida ro’yxat bitta komponentda edi. Endi ilovaga qidiruv maydoni va “topildi: N ta” hisoblagichi qo’shilsin. Ular alohida komponentlar, lekin bitta ma’lumotga tayanadi. Holatni ko’tarish shu vaziyat uchun.

Muammo: ikki komponent bir-birini ko’rmaydi

Ikkita komponent yozamiz. SearchInput qidiruv matnini saqlaydi, TaskList esa ro’yxatni filtrlashi kerak:

// ❌ Bu tuzilma ishlamaydi
import { useState } from 'react';

function SearchInput() {
  const [query, setQuery] = useState("");

  return (
    <input
      type="text"
      value={query}
      onChange={(e) => setQuery(e.target.value)}
      placeholder="Qidirish"
    />
  );
}

function TaskList({ tasks }) {
  // query bu yerda kerak, lekin uni olishning iloji yo'q
  return (
    <ul>
      {tasks.map((task) => (
        <li key={task.id}>{task.title}</li>
      ))}
    </ul>
  );
}

function App() {
  const tasks = [
    { id: 1, title: "Non olish" },
    { id: 2, title: "Kitob o'qish" },
  ];

  return (
    <div>
      <SearchInput />
      <TaskList tasks={tasks} />
    </div>
  );
}

export default App;

Maydonga yozsangiz matn ko’rinadi, lekin ro’yxat qimirlamaydi.

Sabab — query SearchInput ning ichida qulflangan. Props darsidagi qoidani eslang: ma’lumot faqat yuqoridan pastga oqadi. TaskListSearchInput ning bolasi emas, ukasi. Ular orasida hech qanday yo’l yo’q.

App
├── SearchInput   ← query bu yerda
└── TaskList      ← query bu yerda kerak

Yechim: holatni yuqoriga ko’chirish

query ni App ga olib chiqamiz:

import { useState } from 'react';

function SearchInput({ query, onQueryChange }) {
  return (
    <input
      type="text"
      value={query}
      onChange={(e) => onQueryChange(e.target.value)}
      placeholder="Qidirish"
    />
  );
}

function TaskList({ tasks }) {
  if (tasks.length === 0) {
    return <p>Mos vazifa topilmadi.</p>;
  }

  return (
    <ul>
      {tasks.map((task) => (
        <li key={task.id}>{task.title}</li>
      ))}
    </ul>
  );
}

function App() {
  const [query, setQuery] = useState("");

  const tasks = [
    { id: 1, title: "Non olish" },
    { id: 2, title: "Kitob o'qish" },
    { id: 3, title: "Kitob qaytarish" },
  ];

  const visibleTasks = tasks.filter((task) =>
    task.title.toLowerCase().includes(query.toLowerCase())
  );

  return (
    <div>
      <SearchInput query={query} onQueryChange={setQuery} />
      <p>Topildi: {visibleTasks.length} ta</p>
      <TaskList tasks={visibleTasks} />
    </div>
  );
}

export default App;

Maydonga “kitob” deb yozing — ro’yxatda ikkita vazifa qoladi va hisoblagich 2 ni ko’rsatadi. Uchalasi bir vaqtda yangilanadi.

Endi tuzilma shunday:

App                    ← query holati
├── SearchInput        ← query (o'qish) + onQueryChange (yozish)
├── <p>Topildi</p>     ← visibleTasks.length
└── TaskList           ← visibleTasks

Ikki yo’nalishli bog’lanish

Bola holatni o’zgartira olmaydi, lekin o’zgartirishni so’rashi mumkin. Buning uchun ota-komponent unga funksiya beradi:

// Pastga: qiymat
<SearchInput query={query} onQueryChange={setQuery} />
//                ↑ o'qish       ↑ yozish huquqi

Aylanish shunday ishlaydi:

  1. App query ni va setQuery ni SearchInput ga beradi.
  2. Foydalanuvchi harf yozadi.
  3. SearchInput onQueryChange(newQuery) ni chaqiradi.
  4. Bu aslida App dagi setQuery — holat o’zgaradi.
  5. App qayta chiziladi va yangi query ni pastga uzatadi.

To’liq misol: vazifalar ilovasi

Bir necha komponent bitta ro’yxat bilan ishlaydi:

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

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

function TaskList({ tasks, onToggle, onDelete }) {
  if (tasks.length === 0) {
    return <p>Vazifa yo'q.</p>;
  }

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

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

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

  function handleSubmit(e) {
    e.preventDefault();
    if (title.trim() === "") return;

    onAdd(title.trim());
    setTitle("");
  }

  return (
    <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;
// src/App.jsx
import { useState } from 'react';
import TaskForm from './components/TaskForm.jsx';
import TaskList from './components/TaskList.jsx';

function App() {
  const [tasks, setTasks] = useState([
    { id: 1, title: "Non olish", isDone: false },
    { id: 2, title: "Kitob o'qish", isDone: true },
  ]);

  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 activeCount = tasks.filter((task) => !task.isDone).length;

  return (
    <div>
      <h1>Vazifalar</h1>
      <p>Bajarilmagan: {activeCount}</p>
      <TaskForm onAdd={handleAdd} />
      <TaskList tasks={tasks} onToggle={handleToggle} onDelete={handleDelete} />
    </div>
  );
}

export default App;

Brauzerda sinang: vazifa qo’shing, belgilang, o’chiring. Hisoblagich har amalda o’zi to’g’rilanadi.

Diqqat qiling: tasks ni faqat App o’zgartiradi. Qolgan komponentlar ma’lumotni ko’rsatadi va o’zgartirishni so’raydi.

TaskForm nega o’z holatini saqlaydi

Diqqatli o’quvchi savol beradi: TaskForm ichida title holati bor — bu qoidaga zid emasmi?

Yo’q. titlefaqat formaning o’ziga tegishli vaqtinchalik ma’lumot. Foydalanuvchi yozib turgan matn boshqa hech kimga kerak emas; u “Qo’shish” bosilgandagina onAdd orqali yuqoriga uzatiladi.

Qoida shunday:

Ma’lumot Qayerda
Bir nechta komponentga kerak Umumiy ota-komponentda
Faqat bitta komponentga kerak O’sha komponentda

Qachon ko’tarish kerak emas

Holatni keraksiz ko’tarish ham muammo tug’diradi:

1. Ma’lumot faqat bitta komponentniki bo’lsa. Ochiladigan menyuning isOpen holati, forma maydonining matni, kartochkaning “batafsil” ko’rinishi — bularni ko’tarish kerak emas.

// ✅ Holat o'z joyida
function Accordion({ title, children }) {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <div>
      <button type="button" onClick={() => setIsOpen(!isOpen)}>
        {title} {isOpen ? "▲" : "▼"}
      </button>
      {isOpen && <div>{children}</div>}
    </div>
  );
}

export default Accordion;

isOpen ni App ga ko’tarsangiz, App har ochish-yopishda qayta chiziladi va kod sababsiz murakkablashadi.

2. Qiymat mavjud holatdan hisoblanadigan bo’lsa. activeCount uchun alohida holat kerak emas — u tasks dan hisoblanadi. Bu shartli ko’rinish darsida aytilgan qoida.

Holatni qayerga qo’yish kerak

Aniq tartib bor:

  1. Qaysi komponentlar bu ma’lumotga tayanishini aniqlang.
  2. Ularning eng yaqin umumiy ota-komponentini toping.
  3. Holatni o’sha yerga qo’ying.
  4. Qiymatni pastga props bilan uzating.
  5. O’zgartirish kerak bo’lgan bolalarga funksiya uzating.

Misolda: SearchInput, hisoblagich va TaskList — uchalasining eng yaqin umumiy otasi App. Demak query App da turadi.

Amaliy mashq

  1. SearchInput va TaskList ni yozing, query ni SearchInput ichida qoldiring va ro’yxat filtrlanmasligini ko’ring.
  2. query ni App ga ko’taring, SearchInput ga query va onQueryChange props bilan uzating.
  3. “Topildi: N ta” hisoblagichini qo’shing va u ham bir vaqtda yangilanishini tekshiring.
  4. Yuqoridagi to’liq vazifalar ilovasini to’rtta faylga bo’lib yozing: App, TaskForm, TaskList, TaskItem.
  5. TaskForm dagi title holatini App ga ko’tarib ko’ring va kod qanchalik chalkashganini baholang. Keyin qaytaring.
  6. Accordion komponentini yozing va uning isOpen holati nega ko’tarilmasligi kerakligini bir jumlada tushuntiring.

5-qadam teskari tomonni ko’rsatadi: holatni ko’tarish har doim ham yaxshilik emas, faqat zarurat bo’lganda qilinadi.

Qisqacha xulosa

  • Ukalar komponentlar bir-birining holatini ko’ra olmaydi — ma’lumot faqat yuqoridan pastga oqadi.
  • Holatni ko’tarish — umumiy ma’lumotni eng yaqin umumiy ota-komponentga ko’chirish.
  • Ota-komponent pastga qiymatni va o’zgartirish uchun funksiyani uzatadi.
  • Ma’lumot yagona joyda saqlanadi, shuning uchun nusxalari bir-biridan farq qilib qolmaydi.
  • Faqat bitta komponentga tegishli holatni (isOpen, forma maydoni) ko’tarmang.
  • Mavjud holatdan hisoblanadigan qiymat uchun alohida holat kerak emas.
  • Eng yaqin umumiy ota-komponentni tanlang — hammasini App ga yig’ish kodni og’irlashtiradi.

Keyingi darsda yakuniy loyiha — butun bo’lim bo’ylab o’rganilganlarni bitta ilovada birlashtirasiz.

Bu dars foydali bo'ldimi?

marta ko'rildi