useState — ob'ekt va massiv state, nima uchun spread kerak

~15 daq · React · Hooks — Asosiy

Ob'ekt state

State faqat son yoki string emas — ob'ekt ham bo'lishi mumkin.

function Profil() {
  const [user, setUser] = useState({
    ism: 'Jasur',
    email: 'jasur@gmail.com',
    yosh: 25,
  });

  return (
    <div>
      <p>{user.ism}</p>
      <p>{user.email}</p>
    </div>
  );
}

Ob'ektni yangilash — nima uchun spread kerak?

Faqat ismni o'zgartirmoqchisiz. Shunday qilsangiz nima bo'ladi?

// ❌ Xato — email va yosh yo'qoladi!
setUser({ ism: 'Bobur' });

Nima uchun? setUser eski ob'ektni yangi ob'ekt bilan to'liq almashtiradi. { ism: 'Bobur' } — bu faqat ism bor ob'ekt. Email va yosh yo'q.

Oldin:  { ism: 'Jasur', email: 'jasur@gmail.com', yosh: 25 }
Keyin:  { ism: 'Bobur' }   ← email va yosh yo'qoldi!

To'g'ri usul — spread operator bilan eski maydonlarni ko'chirib, faqat o'zgarganini yangilash:

// ✅ To'g'ri — eski maydonlar saqlanadi
setUser({ ...user, ism: 'Bobur' });
...user  →  ism: 'Jasur', email: 'jasur@gmail.com', yosh: 25
ism: 'Bobur'  →  ism ni ustiga yozadi

Natija: { ism: 'Bobur', email: 'jasur@gmail.com', yosh: 25 }

Amaliy misol

function ProfilTahrirlash() {
  const [user, setUser] = useState({
    ism: 'Jasur',
    email: 'jasur@gmail.com',
    shahar: 'Toshkent',
  });

  function ismniOzgartir(yangiIsm) {
    setUser({ ...user, ism: yangiIsm });
  }

  function emailniOzgartir(yangiEmail) {
    setUser({ ...user, email: yangiEmail });
  }

  return (
    <div>
      <input
        value={user.ism}
        onChange={e => ismniOzgartir(e.target.value)}
      />
      <input
        value={user.email}
        onChange={e => emailniOzgartir(e.target.value)}
      />
      <p>Shahar: {user.shahar}</p>
    </div>
  );
}

Yoki qisqaroq — bitta universal handler:

function ProfilTahrirlash() {
  const [user, setUser] = useState({
    ism: 'Jasur',
    email: 'jasur@gmail.com',
    shahar: 'Toshkent',
  });

  function ozgartir(maydon, qiymat) {
    setUser({ ...user, [maydon]: qiymat });
  }

  return (
    <div>
      <input value={user.ism} onChange={e => ozgartir('ism', e.target.value)} />
      <input value={user.email} onChange={e => ozgartir('email', e.target.value)} />
    </div>
  );
}

Massiv state

Massiv state da ham xuddi shu qoida — to'g'ridan-to'g'ri o'zgartirmang, yangi massiv yarating.

Massiv metodlari ikki turga bo'linadi:

Yangi massiv qaytaradi (✅ ishlatish mumkin):
  map, filter, slice, concat, [...spread]

Asl massivni o'zgartiradi (❌ ishlatmang):
  push, pop, splice, sort, reverse

Element qo'shish

const [vazifalar, setVazifalar] = useState([
  { id: 1, matn: 'React o\'rganish' },
]);

// ❌ Xato
vazifalar.push({ id: 2, matn: 'Loyiha yozish' });

// ✅ Oxiriga qo'shish
setVazifalar([...vazifalar, { id: 2, matn: 'Loyiha yozish' }]);

// ✅ Boshiga qo'shish
setVazifalar([{ id: 2, matn: 'Loyiha yozish' }, ...vazifalar]);

Element o'chirish

// ✅ id bo'yicha o'chirish
setVazifalar(vazifalar.filter(v => v.id !== ochiriladiganId));

Element yangilash

// ✅ id bo'yicha yangilash
setVazifalar(vazifalar.map(v =>
  v.id === yangilanadigan.id ? { ...v, matn: yangiMatn } : v
));

To'liq misol — vazifalar ro'yxati

function VazifaIlovasi() {
  const [vazifalar, setVazifalar] = useState([
    { id: 1, matn: 'Nonushta qilish', bajarildi: true },
    { id: 2, matn: 'React o\'rganish', bajarildi: false },
  ]);
  const [yangiMatn, setYangiMatn] = useState('');

  function qoshish() {
    if (!yangiMatn.trim()) return;
    setVazifalar([
      ...vazifalar,
      { id: Date.now(), matn: yangiMatn, bajarildi: false },
    ]);
    setYangiMatn('');
  }

  function bajarildiToggle(id) {
    setVazifalar(vazifalar.map(v =>
      v.id === id ? { ...v, bajarildi: !v.bajarildi } : v
    ));
  }

  function ochirish(id) {
    setVazifalar(vazifalar.filter(v => v.id !== id));
  }

  return (
    <div>
      <div>
        <input
          value={yangiMatn}
          onChange={e => setYangiMatn(e.target.value)}
          placeholder="Yangi vazifa..."
        />
        <button onClick={qoshish}>Qo'shish</button>
      </div>

      <ul>
        {vazifalar.map(v => (
          <li key={v.id}>
            <input
              type="checkbox"
              checked={v.bajarildi}
              onChange={() => bajarildiToggle(v.id)}
            />
            <span style={{ textDecoration: v.bajarildi ? 'line-through' : 'none' }}>
              {v.matn}
            </span>
            <button onClick={() => ochirish(v.id)}>✕</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

Xulosa

Ob'ekt yangilash:
  setUser({ ...user, ozgarganMaydon: yangiQiymat })
         └── eski maydonlar  └── faqat shu o'zgaradi

Massiv yangilash:
  Qo'shish:   [...massiv, yangiElement]
  O'chirish:  massiv.filter(el => el.id !== id)
  Yangilash:  massiv.map(el => el.id === id ? {...el, ...yangi} : el)

Asosiy qoida: React state ni reference bo'yicha solishtiradi. Agar siz eski ob'ekt/massivni o'zgartirsangiz — React farq ko'rmaydi, ekran yangilanmaydi. Har doim yangi ob'ekt/massiv yarating.


Keyingi dars

10-dars: useEffect — nima, qachon ishlaydi, dependency array