React useState: holat bilan ishlash

Hodisalar darsi ochiq muammo bilan tugadi: tugma bosilganda count o’zgarardi, lekin ekran qotib turardi. Sabab ikkita edi — React o’zgarishdan xabardor bo’lmadi va qiymat qayta chizishda yo’qoldi. useState ikkalasini ham hal qiladi.

useState — birinchi hook

Muammoli kodni tuzatamiz:

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <div>
      <p>Bosilgan: {count}</p>
      <button type="button" onClick={handleClick}>
        Bosish
      </button>
    </div>
  );
}

export default Counter;

Endi tugmani bosing — ekrandagi raqam oshadi.

useState nima qaytaradi

useState ikki elementli massiv qaytaradi:

const [count, setCount] = useState(0);
Element Nima
count Joriy qiymat
setCount Qiymatni o’zgartiradigan funksiya
useState(0) dagi 0 Boshlang’ich qiymat

Kvadrat qavs — massiv destructuring. Nomlarni o’zingiz tanlaysiz, lekin kelishuv qat’iy: qiymat x bo’lsa, funksiya setX.

const [name, setName] = useState("");
const [isOpen, setIsOpen] = useState(false);
const [tasks, setTasks] = useState([]);
const [user, setUser] = useState(null);

Boshlang’ich qiymat faqat birinchi chizishda ishlatiladi. Keyingi chizishlarda useState saqlangan qiymatni qaytaradi — shuning uchun raqam nolga qaytmaydi.

Qayta chizish qanday ishlaydi

setCount chaqirilganda ketma-ketlik shunday:

  1. React yangi qiymatni yozib qo’yadi.
  2. Komponent funksiyasini qaytadan chaqiradi.
  3. Bu safar useState yangi qiymatni qaytaradi.
  4. Yangi JSX hosil bo’ladi va eskisi bilan solishtiriladi.
  5. DOM’da faqat farq o’zgartiriladi.

Muhim natija: komponent funksiyasi ko’p marta ishlaydi. Har bosishda Counter funksiyasi boshidan oxirigacha qayta bajariladi. Shuning uchun uning ichida og’ir hisob yozmaslik kerak.

Holatni to’g’ridan-to’g’ri o’zgartirmang

Bu qoidaning buzilishi eng ko’p uchraydigan React xatosi:

// ❌ Ishlamaydi
function handleClick() {
  count++;
}

// ❌ Bu ham ishlamaydi
function handleClick() {
  count = count + 1;
}

// ✅ To'g'ri
function handleClick() {
  setCount(count + 1);
}

count — oddiy const o’zgaruvchi. Uni o’zgartirish React’ga hech nima demaydi. Faqat setCount chaqiruvi qayta chizishni boshlaydi.

Oldingi qiymatga tayanish

Yuqoridagi xususiyat aniq muammoga olib keladi. Tugmani bir bosishda ikki marta oshirmoqchi bo’lsangiz:

// ❌ Bir marta oshadi
function handleDoubleClick() {
  setCount(count + 1);
  setCount(count + 1);
}

count 0 bo’lsa, ikkala qator ham setCount(1) ga aylanadi. Natija 1, 2 emas.

Yechim — set funksiyasiga qiymat emas, funksiya berish:

// ✅ Ikki marta oshadi
function handleDoubleClick() {
  setCount((previousCount) => previousCount + 1);
  setCount((previousCount) => previousCount + 1);
}

Bunday yozuvda React har chaqiruvga eng oxirgi qiymatni beradi. Birinchisi 0 olib 1 qaytaradi, ikkinchisi 1 olib 2 qaytaradi.

Bir necha holat

Komponentda useState ni xohlagancha chaqirish mumkin:

import { useState } from 'react';

function TaskForm() {
  const [title, setTitle] = useState("");
  const [priority, setPriority] = useState(1);
  const [isUrgent, setIsUrgent] = useState(false);

  return (
    <div>
      <p>Nomi: {title || "(bo'sh)"}</p>
      <p>Muhimlik: {priority}</p>
      <p>Shoshilinch: {isUrgent ? "ha" : "yo'q"}</p>
      <button type="button" onClick={() => setIsUrgent(!isUrgent)}>
        Shoshilinchni almashtirish
      </button>
    </div>
  );
}

export default TaskForm;

Tugma bosilganda faqat “Shoshilinch” qatori o’zgaradi — qolgan holatlar joyida qoladi.

Har holat mustaqil. Bog’liq bo’lmagan ma’lumotlarni alohida saqlang — bitta katta obyektga tiqishdan ko’ra shunday qilish o’qishga qulay.

Obyekt holati

Holat obyekt bo’lsa, uni o’zgartirmang — yangi nusxa yasang:

const [user, setUser] = useState({ name: "Aziz", city: "Toshkent" });

// ❌ React o'zgarishni sezmaydi
function handleRename() {
  user.name = "Bobur";
  setUser(user);
}

// ✅ Yangi obyekt
function handleRename() {
  setUser({ ...user, name: "Bobur" });
}

Birinchi variant ishlamaydi, chunki React eski va yangi qiymatni solishtirganda bir xil obyekt ko’radi: manzil o’zgarmagan, demak o’zgarish yo’q deb hisoblaydi.

{ ...user, name: "Bobur" } esa yangi obyekt: user ning barcha maydonlari ko’chiriladi, name esa yangisiga almashtiriladi.

// asl: { name: "Aziz", city: "Toshkent" }
// yangi: { name: "Bobur", city: "Toshkent" }

Bu funksional dasturlash darsidagi o’zgarmaslik qoidasi. React shunga tayanadi.

Massiv holati

Massivda ham xuddi shu mantiq. push, splice, sort — hammasi asl massivni o’zgartiradi, demak yaramaydi.

Uchta asosiy amal:

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

Qo’shish — spread bilan:

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

O’chirishfilter bilan:

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

O’zgartirishmap bilan:

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

Oxirgisini o’qing: har vazifa uchun id tekshiriladi. Mos kelsa yangi obyekt qaytariladi (isDone teskarisiga o’zgargan), mos kelmasa vazifaning o’zi qoladi.

Qulay jadval:

Amal Ishlating Ishlatmang
Qo’shish [...tasks, newTask] tasks.push()
Boshiga qo’shish [newTask, ...tasks] tasks.unshift()
O’chirish tasks.filter(...) tasks.splice()
O’zgartirish tasks.map(...) tasks[0].isDone = true
Saralash [...tasks].sort(...) tasks.sort()

To’liq misol

Hamma narsani birlashtiramiz:

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

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

  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));
  }

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

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

  return (
    <div>
      <h1>Vazifalar</h1>
      <p>Bajarilmagan: {activeCount}</p>
      <button type="button" onClick={handleAdd}>
        Qo'shish
      </button>
      <ul>
        {tasks.map((task) => (
          <li key={task.id}>
            <input
              type="checkbox"
              checked={task.isDone}
              onChange={() => handleToggle(task.id)}
            />
            <span className={task.isDone ? "done" : "active"}>
              {task.title}
            </span>
            <button type="button" onClick={() => handleDelete(task.id)}>
              O'chirish
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default App;

Brauzerda sinab ko’ring: checkbox belgilansa matn ko’rinishi o’zgaradi, “O’chirish” qator yo’qotadi, “Qo’shish” yangi qator qo’shadi. Va har amalda “Bajarilmagan” raqami o’zi to’g’rilanadi.

React nima darsidagi muammoni eslang: qo’lda DOM boshqarishda hisoblagichni yangilashni unutish mumkin edi. Bu yerda activeCount har chizishda tasks dan hisoblanadi, demak uni unutish imkonsiz.

Amaliy mashq

  1. Counter komponentini useState bilan yozing va tugma bosilganda ekrandagi raqam oshishini tekshiring.
  2. handleClick ichida setCount(count + 1) dan keyin console.log(count) qo’ying — eski qiymat chiqishini ko’ring.
  3. Bitta ishlovchi ichida setCount(count + 1) ni ikki marta yozing, raqam nechtaga oshganini sanang. Keyin funksiya shakliga o’tkazing va farqni ko’ring.
  4. isOpen nomli mantiqiy holat yarating, tugma bilan uni almashtiring: setIsOpen(!isOpen).
  5. Yuqoridagi to’liq misolni loyihangizga ko’chiring va uchta amalni ham sinab ko’ring.
  6. handleAdd ichida setTasks([...tasks, newTask]) o’rniga tasks.push(newTask); setTasks(tasks); yozing. Ekranda hech nima o’zgarmasligini ko’ring, keyin qaytaring.

3-va 6-qadamlar React’dagi ikki asosiy chalkashlikni ko’rsatadi: holatning kechikib yangilanishi va o’zgarmaslik talabi.

Qisqacha xulosa

  • useState komponentga o’zgaruvchan holat qo’shadi va [value, setValue] juftligini qaytaradi.
  • Boshlang’ich qiymat faqat birinchi chizishda ishlatiladi.
  • Faqat set funksiyasi qayta chizishni boshlaydi — o’zgaruvchini to’g’ridan-to’g’ri o’zgartirish hech nima qilmaydi.
  • set chaqirilgandan keyin qiymat darrov o’zgarmaydi; yangisi keyingi chizishda keladi.
  • Yangi qiymat eskisiga bog’liq bo’lsa funksiya shaklini ishlating: setCount((previous) => previous + 1).
  • Obyekt va massiv holatini o’zgartirmang{ ...user }, [...tasks], map, filter bilan yangi nusxa yasang.
  • Hook’lar komponentning eng yuqorisida, shart va sikl ichisiz chaqiriladi.

Keyingi darsda forma va boshqariladigan elementlar — foydalanuvchidan matn qabul qilish.

Bu dars foydali bo'ldimi?

marta ko'rildi