useSearchParams — query parametrlar bilan ishlash

~15 daq · React · Routing

Muammodan boshlaylik

Siz mahsulotlar sahifasini yozyapsiz. Foydalanuvchi:

  • Narx bo'yicha saraladi
  • "Elektronika" kategoriyasini tanladi
  • Qidiruvga "iPhone" deb yozdi
  • 3-sahifaga o'tdi

Hammasi ajoyib ishlayapti. Lekin foydalanuvchi sahifani yangiladi — hamma filtr va qidiruv yo'qoldi. Boshidan boshlashiga to'g'ri keldi.

Yoki do'stiga havolani yubormoqchi — lekin havola faqat /mahsulotlar ko'rsatadi, filtrlar saqlanmagan.

Muammo: filtr holati useState da — u faqat xotirada, URL da emas.

Yechim: filtrlarni URL ga saqlash. ?kategoriya=elektronika&tartib=narx&sahifa=3 ko'rinishida. Sahifa yangilansin, havola ulashilsin — hamma narsa saqlanadi.

Bu — query parametrlar (search params). useSearchParams shu bilan ishlaydi.


Query parametr nima?

URL ning ? dan keyingi qismi — query string:

/mahsulotlar?kategoriya=elektronika&tartib=narx&sahifa=3
             ↑
             query string boshlanadi

kategoriya, tartib, sahifa — kalitlar. elektronika, narx, 3 — qiymatlar.

useParams :id kabi dinamik segmentlarni oladi. useSearchParams esa ?kalit=qiymat query parametrlarini oladi.

Hayotiy o'xshatma: kutubxona katalogi. Kitob ID si — /kitob/42 (useParams). Qidiruv filtrlari esa — /kitoblar?janr=fantastika&yil=2023&sahifa=2 (useSearchParams). Ikkinchisi sharoitga qarab o'zgaradi, URL da ko'rinadi, ulashish va orqaga qaytish ishlaydi.


Asosiy ishlatilish

useSearchParams — useState ga o'xshash: [qiymat, setter] qaytaradi.

import { useSearchParams } from 'react-router-dom';

function Mahsulotlar() {
  const [searchParams, setSearchParams] = useSearchParams();

  // Qiymat olish
  const kategoriya = searchParams.get('kategoriya'); // "elektronika" yoki null
  const sahifa = searchParams.get('sahifa');         // "3" yoki null

  return <div>Kategoriya: {kategoriya}</div>;
}

searchParams.get('kalit') — qiymat qaytaradi yoki null.


Query parametrlarni o'zgartirish

Yozish — setSearchParams

// Bitta parametr o'rnatish
setSearchParams({ kategoriya: 'elektronika' });
// URL: /mahsulotlar?kategoriya=elektronika

// Bir nechta parametr
setSearchParams({ kategoriya: 'elektronika', tartib: 'narx', sahifa: '1' });
// URL: /mahsulotlar?kategoriya=elektronika&tartib=narx&sahifa=1

Mavjud parametrni saqlab yangilanish

// ❌ Muammo — boshqa parametrlar o'chib ketadi
setSearchParams({ sahifa: '2' });
// Agar oldin kategoriya=elektronika bo'lsa — yo'qoladi!

// ✅ To'g'ri — mavjud parametrlarni saqlab, faqat kerakligini o'zgartirish
setSearchParams((oldingi) => {
  const yangi = new URLSearchParams(oldingi);
  yangi.set('sahifa', '2');
  return yangi;
});

Funksiya shakli useState setter ga o'xshaydi — oldingi holatni olib, yangi holatni qaytaradi.


URLSearchParams — yordamchi metodlar

searchParams — URLSearchParams ob'ekti. Bir nechta foydali metodi bor:

const [searchParams] = useSearchParams();

searchParams.get('kategoriya');       // bitta qiymat olish
searchParams.getAll('tag');           // bir xil kalitdagi barcha qiymatlar
searchParams.has('qidiruv');          // parametr bormi — boolean
searchParams.toString();              // "kategoriya=elektronika&sahifa=1"

getAll — bir kalit bir nechta marta kelganda:

/mahsulotlar?tag=yangi&tag=ommabop&tag=chegirma
searchParams.getAll('tag'); // ["yangi", "ommabop", "chegirma"]

To'liq misol — filtrli mahsulotlar sahifasi

// pages/Mahsulotlar.jsx
import { useSearchParams } from 'react-router-dom';

const barcha = [
  { id: 1, nomi: 'iPhone 15', kategoriya: 'telefon', narxi: 12000000 },
  { id: 2, nomi: 'MacBook Pro', kategoriya: 'noutbuk', narxi: 35000000 },
  { id: 3, nomi: 'Samsung S24', kategoriya: 'telefon', narxi: 10000000 },
  { id: 4, nomi: 'Dell XPS', kategoriya: 'noutbuk', narxi: 28000000 },
  { id: 5, nomi: 'iPad Air', kategoriya: 'planshet', narxi: 15000000 },
];

function Mahsulotlar() {
  const [searchParams, setSearchParams] = useSearchParams();

  const kategoriya = searchParams.get('kategoriya') || 'hammasi';
  const tartib = searchParams.get('tartib') || 'ism';

  // Filtrlash
  const filtrlangan = barcha
    .filter((m) =>
      kategoriya === 'hammasi' ? true : m.kategoriya === kategoriya
    )
    .sort((a, b) =>
      tartib === 'narx' ? a.narxi - b.narxi : a.nomi.localeCompare(b.nomi)
    );

  function kategoriyaOzgartir(yangiKategoriya) {
    setSearchParams((oldingi) => {
      const yangi = new URLSearchParams(oldingi);
      yangi.set('kategoriya', yangiKategoriya);
      yangi.set('sahifa', '1'); // ← kategoriya o'zgarganda 1-sahifaga qayt
      return yangi;
    });
  }

  function tartibOzgartir(yangiTartib) {
    setSearchParams((oldingi) => {
      const yangi = new URLSearchParams(oldingi);
      yangi.set('tartib', yangiTartib);
      return yangi;
    });
  }

  return (
    <div>
      <h1>Mahsulotlar</h1>

      {/* Kategoriya filtri */}
      <div>
        {['hammasi', 'telefon', 'noutbuk', 'planshet'].map((k) => (
          <button
            key={k}
            onClick={() => kategoriyaOzgartir(k)}
            style={{ fontWeight: kategoriya === k ? 'bold' : 'normal' }}
          >
            {k}
          </button>
        ))}
      </div>

      {/* Tartib filtri */}
      <select
        value={tartib}
        onChange={(e) => tartibOzgartir(e.target.value)}
      >
        <option value="ism">Ism bo'yicha</option>
        <option value="narx">Narx bo'yicha</option>
      </select>

      {/* Natijalar */}
      <ul>
        {filtrlangan.map((m) => (
          <li key={m.id}>
            {m.nomi} — {m.narxi.toLocaleString()} so'm
          </li>
        ))}
      </ul>
    </div>
  );
}

export default Mahsulotlar;

Endi foydalanuvchi ?kategoriya=telefon&tartib=narx havolasini ulashsa — boshqa odam ham aynan shu filtrlangan ko'rinishni ko'radi.


Qidiruv satri — debounce bilan

Har harf bosishda URL o'zgarsa — brauzer tarixi to'lib ketadi. replace: true bilan bu muammo hal bo'ladi:

import { useSearchParams } from 'react-router-dom';
import { useState, useEffect } from 'react';

function QidiruvSatri() {
  const [searchParams, setSearchParams] = useSearchParams();
  const [matn, setMatn] = useState(searchParams.get('q') || '');

  useEffect(() => {
    const taymer = setTimeout(() => {
      setSearchParams(
        (oldingi) => {
          const yangi = new URLSearchParams(oldingi);
          if (matn) {
            yangi.set('q', matn);
          } else {
            yangi.delete('q'); // ← bo'sh bo'lsa parametrni o'chir
          }
          return yangi;
        },
        { replace: true } // ← har harf alohida tarix yozuvi bo'lmasin
      );
    }, 400); // ← 400ms kutib, keyin URL ni yangilaydi

    return () => clearTimeout(taymer);
  }, [matn, setSearchParams]);

  return (
    <input
      value={matn}
      onChange={(e) => setMatn(e.target.value)}
      placeholder="Qidirish..."
    />
  );
}

Sahifalash (pagination) bilan

function Mahsulotlar() {
  const [searchParams, setSearchParams] = useSearchParams();

  const sahifa = Number(searchParams.get('sahifa')) || 1;
  const hajm = 10; // har sahifada nechta

  function sahifaOzgartir(yangiSahifa) {
    setSearchParams((oldingi) => {
      const yangi = new URLSearchParams(oldingi);
      yangi.set('sahifa', String(yangiSahifa));
      return yangi;
    });
  }

  return (
    <div>
      {/* mahsulotlar ro'yxati */}

      <div>
        <button
          disabled={sahifa === 1}
          onClick={() => sahifaOzgartir(sahifa - 1)}
        >
          ← Oldingi
        </button>

        <span>Sahifa: {sahifa}</span>

        <button onClick={() => sahifaOzgartir(sahifa + 1)}>
          Keyingi →
        </button>
      </div>
    </div>
  );
}

Link bilan query parametr

<Link> ga ham query parametrlar uzatish mumkin:

import { Link } from 'react-router-dom';

// Matn ko'rinishida
<Link to="/mahsulotlar?kategoriya=telefon&tartib=narx">
  Telefonlar
</Link>

// Ob'ekt ko'rinishida
<Link to={{ pathname: '/mahsulotlar', search: '?kategoriya=telefon' }}>
  Telefonlar
</Link>

Tez-tez uchraydigan xatolar

1. Mavjud parametrlarni o'chirmay yangilash

// ❌ Xato — boshqa parametrlar yo'qoladi
setSearchParams({ sahifa: '2' });
// Oldin: ?kategoriya=telefon&sahifa=1
// Keyin: ?sahifa=2  ← kategoriya yo'qoldi!

// ✅ To'g'ri — funksiya shakli bilan
setSearchParams((oldingi) => {
  const yangi = new URLSearchParams(oldingi);
  yangi.set('sahifa', '2');
  return yangi;
  // Keyin: ?kategoriya=telefon&sahifa=2
});

Sababi: setSearchParams({...}) to'liq almashtiradi — ko'rsatilmagan parametrlar o'chib ketadi. Funksiya shakli esa oldingi holatni saqlab, faqat kerakligini o'zgartiradi.


2. Qiymatni raqamga o'girmaslik

// ❌ Xato — string bilan hisoblash
const sahifa = searchParams.get('sahifa'); // "3"
const keyingi = sahifa + 1; // "31" — string concatenation!

// ✅ To'g'ri — Number() bilan o'girish
const sahifa = Number(searchParams.get('sahifa')) || 1;
const keyingi = sahifa + 1; // 4

Sababi: searchParams.get() har doim string qaytaradi. Matematik amallar uchun raqamga o'girish shart.


3. Yo'q parametrni ishlatganda null ni hisobga olmaslik

// ❌ Xato — null bo'lishi mumkin
const kategoriya = searchParams.get('kategoriya');
// URL da ?kategoriya yo'q bo'lsa — null
kategoriya.toUpperCase(); // TypeError: Cannot read properties of null

// ✅ To'g'ri — default qiymat bilan
const kategoriya = searchParams.get('kategoriya') || 'hammasi';

Sababi: Parametr URL da bo'lmasa get() null qaytaradi. Har doim default qiymat yoki ?. optional chaining ishlatish kerak.


4. Qidiruv satriga debounce qo'ymaslik

// ❌ Xato — har harf bosishda URL o'zgaradi
function QidiruvSatri() {
  const [searchParams, setSearchParams] = useSearchParams();

  return (
    <input
      onChange={(e) => setSearchParams({ q: e.target.value })}
    />
  );
  // Foydalanuvchi "iPhone" yozsa: brauzer tarixida
  // i → iP → iPh → iPho → iPhon → iPhone — 6 ta yozuv!
}

Sababi: Har harf bosishda setSearchParams chaqirilsa — brauzer tarixiga alohida yozuv qo'shiladi. "Orqaga" tugmasi harfma-harf qaytadi. Yechim: debounce va replace: true.


Xulosa

  • useSearchParams — URL dagi ?kalit=qiymat bilan ishlash
  • searchParams.get('kalit') — qiymat olish (string yoki null)
  • setSearchParams({ ... }) — to'liq almashtiradi
  • setSearchParams(oldingi => ...) — mavjud parametrlarni saqlab yangilaydi
  • replace: true — qidiruv satri uchun, tarixga ortiqcha yozmaslik
  • Qiymatlar har doim string — raqam kerak bo'lsa Number() ishlatish
  • null bo'lishi mumkin — default qiymat bilan himoya qilish

Keyingi dars: 45-dars: Nested Routes — ichma-ich route lar, Outlet