useReducer + useContext — o'z state manager ini yozish

~25 daq · React · Hooks — Ilg'or

Ikkalasini birlashtirish

Oldingi darslarda ikki narsa o'rgandik:

  • useContext — ma'lumotni istalgan komponentga yetkazadi
  • useReducer — murakkab state o'zgarishlarini tartibli boshqaradi

Ikkalasini birlashtirish — Redux ga o'xshash oddiy global state manager hosil qiladi. Qo'shimcha kutubxona kerak emas.

Tasavvur qiling: savat state bor. Uni istalgan komponentdan o'zgartirish kerak. useReducer — o'zgartirish mantiqini boshqaradi. useContext — state va dispatch ni istalgan komponentga yetkazadi.

useReducer  →  state va dispatch yaratadi
useContext  →  ularni istalgan komponentga yetkazadi

Natija:
  istalgan komponent
    dispatch({ type: 'QOSHISH' })  →  global state o'zgardi
    state.mahsulotlar              →  global state dan o'qish

To'liq misol — savat

// SavatContext.jsx
import { createContext, useContext, useReducer } from 'react';

// Reducer — barcha mantiq shu yerda
function savatReducer(state, action) {
  switch (action.type) {

    case 'QOSHISH': {
      const bor = state.find(m => m.id === action.payload.id);
      if (bor) {
        // allaqachon bor — sonini oshir
        return state.map(m =>
          m.id === action.payload.id ? { ...m, son: m.son + 1 } : m
        );
      }
      // yangi mahsulot — qo'sh
      return [...state, { ...action.payload, son: 1 }];
    }

    case 'KAMAYTIR':
      return state
        .map(m => m.id === action.payload ? { ...m, son: m.son - 1 } : m)
        .filter(m => m.son > 0); // son 0 ga tushsa — o'chir

    case 'OCHIRISH':
      return state.filter(m => m.id !== action.payload);

    case 'TOZALA':
      return [];

    default:
      return state;
  }
}

// Context
const SavatContext = createContext();

// Provider — App ni o'raydi
export function SavatProvider({ children }) {
  const [savat, dispatch] = useReducer(savatReducer, []);

  // Hisoblangan qiymatlar — har render da yangilanadi
  const jami = savat.reduce((sum, m) => sum + m.narx * m.son, 0);
  const soni = savat.reduce((sum, m) => sum + m.son, 0);

  return (
    <SavatContext.Provider value={{ savat, dispatch, jami, soni }}>
      {children}
    </SavatContext.Provider>
  );
}

// Custom hook
export function useSavat() {
  const context = useContext(SavatContext);
  if (!context) throw new Error('useSavat SavatProvider ichida ishlatilishi kerak');
  return context;
}

Endi istalgan komponentda useSavat() chaqiriladi:

// Navbar.jsx
function Navbar() {
  const { soni } = useSavat();
  return <nav>🛒 {soni}</nav>;
}
// MahsulotKarta.jsx
function MahsulotKarta({ mahsulot }) {
  const { dispatch } = useSavat();

  return (
    <div>
      <h3>{mahsulot.nomi}</h3>
      <button onClick={() => dispatch({ type: 'QOSHISH', payload: mahsulot })}>
        Savatga qo'shish
      </button>
    </div>
  );
}
// SavatSahifa.jsx
function SavatSahifa() {
  const { savat, dispatch, jami } = useSavat();

  if (savat.length === 0) return <p>Savat bo'sh</p>;

  return (
    <div>
      {savat.map(m => (
        <div key={m.id}>
          <span>{m.nomi}</span>
          <button onClick={() => dispatch({ type: 'KAMAYTIR', payload: m.id })}>−</button>
          <span>{m.son}</span>
          <button onClick={() => dispatch({ type: 'QOSHISH', payload: m })}>+</button>
          <button onClick={() => dispatch({ type: 'OCHIRISH', payload: m.id })}>✕</button>
        </div>
      ))}
      <strong>Jami: {jami.toLocaleString()} so'm</strong>
      <button onClick={() => dispatch({ type: 'TOZALA' })}>Savatni tozala</button>
    </div>
  );
}

Arxitektura

SavatProvider
├── savatReducer  ← barcha mantiq shu yerda
├── SavatContext  ← state va dispatch tarqatiladi
│
├── Navbar        → useSavat() → soni
├── MahsulotKarta → useSavat() → dispatch(QOSHISH)
└── SavatSahifa   → useSavat() → savat, dispatch, jami

Har bir komponent faqat kerakli narsani oladi. Mantiq bir joyda — savatReducer. Yangi amal kerak bo'lsa — faqat reducerga qo'shiladi, komponentlarga tegmaydi.


Bu pattern qachon yetarli, qachon emas?

useReducer + useContext:
  ✅ O'rta murakkablikdagi global state
  ✅ Qo'shimcha kutubxona kerak emas
  ✅ Redux ni o'rganishdan oldin tushunish uchun yaxshi
  ❌ Tez-tez o'zgaradigan katta state da sekin
     (har o'zgarishda barcha consumer qayta chiziladi)
  ❌ DevTools yo'q

Zustand:
  ✅ Oddiyroq sintaksis
  ✅ Tez-tez o'zgaradigan state uchun optimallashtirilgan
  ✅ Minimal kod

Savat, foydalanuvchi, tema — useReducer + useContext yetarli. Real-time dashboard, tez-tez yangilanadigan katta ro'yxat — Zustand yaxshiroq.


Xulosa

useReducer + useContext = oddiy global state manager.

Uchta qism:

  1. Reducer — mantiq (qanday o'zgarish)
  2. Provider — state va dispatch ni tarqatish
  3. Custom hook — qulay interfeys

Zustand va Redux ni o'rganishdan oldin bu pattern ni tushunish — ularning ichini tushunishga yordam beradi. Ular ham xuddi shu g'oyaga asoslangan, faqat optimallashtirilgan.


Keyingi dars

22-dars: useLayoutEffect — useEffect dan farqi, qachon kerak