Protected Route — faqat login qilganlar uchun
Muammodan boshlaylik
Siz bank ilovasini yozyapsiz. /dashboard, /hisoblar, /o'tkazmalar
sahifalari faqat login qilgan foydalanuvchilar uchun.
Hozirgi holat: /dashboard manzilini brauzerga yozgan har kim —
login qilmagan bo'lsa ham — sahifani ko'ra oladi.
Bu jiddiy muammo. Har bir sahifa komponentiga tekshiruv qo'yish ham yaxshi emas — kod takrorlanadi va biror sahifada unutib qolish mumkin.
Yechim: bitta ProtectedRoute komponenti — u login holatini
tekshiradi. Login qilingan bo'lsa o'tkazib yuboradi, bo'lmasa
/kirish sahifasiga yo'naltiradi. Barcha himoyalangan sahifalar
shu komponent orqali o'tadi.
Protected Route qanday ishlaydi?
ProtectedRoute — oddiy komponent. U ikkita narsa qiladi:
- Foydalanuvchi login qilganmi — tekshiradi
- Ha —
<Outlet />(bola Route) ni ko'rsatadi - Yo'q —
/kirishsahifasiga yo'naltiradi
Hayotiy o'xshatma: ofis kiraverishdagi qo'riqchi. Xodimlar kartasini ko'rsatsangiz — o'tkazib yuboradi. Ko'rsatmasangiz — "Iltimos, ro'yxatdan o'ting" deydi. Har bir xonaga alohida qo'riqchi qo'yishning hojati yo'q — bitta kiraverishda tekshiruv.
Eng oddiy variant
// components/ProtectedRoute.jsx
import { Navigate, Outlet } from 'react-router-dom';
function ProtectedRoute({ foydalanuvchi }) {
if (!foydalanuvchi) {
return <Navigate to="/kirish" replace />;
}
return <Outlet />;
}
export default ProtectedRoute;
<Navigate to="/kirish" replace /> — foydalanuvchini /kirish
sahifasiga yo'naltiradi. replace — tarixga qo'shmaslik uchun
(login dan keyin "orqaga" tugmasi himoyalangan sahifaga qaytmasin).
App.jsx da ishlatish
// App.jsx
import { Routes, Route } from 'react-router-dom';
import ProtectedRoute from './components/ProtectedRoute';
import { useAuth } from './hooks/useAuth';
import Bosh from './pages/Bosh';
import Kirish from './pages/Kirish';
import Dashboard from './pages/Dashboard';
import Profil from './pages/Profil';
import Sozlamalar from './pages/Sozlamalar';
import TopilmadI from './pages/TopilmadI';
function App() {
const { foydalanuvchi } = useAuth();
return (
<Routes>
{/* Ochiq sahifalar — hamma ko'ra oladi */}
<Route path="/" element={<Bosh />} />
<Route path="/kirish" element={<Kirish />} />
{/* Himoyalangan sahifalar — faqat login qilganlar */}
<Route element={<ProtectedRoute foydalanuvchi={foydalanuvchi} />}>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profil" element={<Profil />} />
<Route path="/sozlamalar" element={<Sozlamalar />} />
</Route>
<Route path="*" element={<TopilmadI />} />
</Routes>
);
}
ProtectedRoute — path siz Route. U faqat wrapper vazifasini
o'taydi: tekshiradi va <Outlet /> orqali bola Route larni chiqaradi.
useAuth — autentifikatsiya hook i
Amaliy loyihalarda foydalanuvchi holati global state da saqlanadi.
Oddiy useAuth hook misoli:
// hooks/useAuth.js
import { createContext, useContext, useState } from 'react';
const AuthContext = createContext(null);
export function AuthProvider({ children }) {
const [foydalanuvchi, setFoydalanuvchi] = useState(
() => JSON.parse(localStorage.getItem('foydalanuvchi')) || null
);
function kirish(malumot) {
setFoydalanuvchi(malumot);
localStorage.setItem('foydalanuvchi', JSON.stringify(malumot));
}
function chiqish() {
setFoydalanuvchi(null);
localStorage.removeItem('foydalanuvchi');
}
return (
<AuthContext.Provider value={{ foydalanuvchi, kirish, chiqish }}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
return useContext(AuthContext);
}
// main.jsx
import { AuthProvider } from './hooks/useAuth';
<BrowserRouter>
<AuthProvider>
<App />
</AuthProvider>
</BrowserRouter>
Endi useAuth ni ilovaning istalgan joyida chaqirish mumkin.
ProtectedRoute — useAuth bilan
foydalanuvchi ni prop orqali emas, hook orqali olsa — App.jsx
da prop uzatish shart emas:
// components/ProtectedRoute.jsx
import { Navigate, Outlet } from 'react-router-dom';
import { useAuth } from '../hooks/useAuth';
function ProtectedRoute() {
const { foydalanuvchi } = useAuth(); // ← prop emas, hook
if (!foydalanuvchi) {
return <Navigate to="/kirish" replace />;
}
return <Outlet />;
}
export default ProtectedRoute;
// App.jsx — prop uzatish shart emas
<Route element={<ProtectedRoute />}>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profil" element={<Profil />} />
</Route>
Qaytish manzilini saqlash
Foydalanuvchi /dashboard ga kirmoqchi bo'ldi — login sahifasiga
yo'naltirildi. Login qilgandan keyin — /dashboard ga qaytishi kerak,
/bosh-sahifa ga emas.
Buning uchun state orqali qaytish manzilini uzatamiz:
// components/ProtectedRoute.jsx
import { Navigate, Outlet, useLocation } from 'react-router-dom';
import { useAuth } from '../hooks/useAuth';
function ProtectedRoute() {
const { foydalanuvchi } = useAuth();
const location = useLocation(); // ← joriy manzil
if (!foydalanuvchi) {
return (
<Navigate
to="/kirish"
state={{ from: location.pathname }} // ← qaytish manzili saqlanadi
replace
/>
);
}
return <Outlet />;
}
export default ProtectedRoute;
// pages/Kirish.jsx
import { useNavigate, useLocation } from 'react-router-dom';
import { useAuth } from '../hooks/useAuth';
function Kirish() {
const { kirish } = useAuth();
const navigate = useNavigate();
const location = useLocation();
const qaytishManzili = location.state?.from || '/dashboard'; // ← qaytish manzili
async function yuborish(e) {
e.preventDefault();
// ... server bilan ishlash ...
await kirish({ ism: 'Jasur', email: 'jasur@misol.uz' });
navigate(qaytishManzili, { replace: true }); // ← o'sha sahifaga qaytish
}
return (
<form onSubmit={yuborish}>
<input name="email" type="email" placeholder="Email" />
<input name="parol" type="password" placeholder="Parol" />
<button type="submit">Kirish</button>
</form>
);
}
export default Kirish;
Rol asosida himoya — admin sahifalari
Ba'zi sahifalar faqat admin uchun. ProtectedRoute ga rol
tekshiruvi qo'shish mumkin:
// components/ProtectedRoute.jsx
import { Navigate, Outlet, useLocation } from 'react-router-dom';
import { useAuth } from '../hooks/useAuth';
function ProtectedRoute({ kerakliRol }) {
const { foydalanuvchi } = useAuth();
const location = useLocation();
// Login qilinmaganmi?
if (!foydalanuvchi) {
return <Navigate to="/kirish" state={{ from: location.pathname }} replace />;
}
// Kerakli rol bormi?
if (kerakliRol && foydalanuvchi.rol !== kerakliRol) {
return <Navigate to="/ruxsat-yoq" replace />;
}
return <Outlet />;
}
export default ProtectedRoute;
// App.jsx
<Routes>
<Route path="/" element={<Bosh />} />
<Route path="/kirish" element={<Kirish />} />
<Route path="/ruxsat-yoq" element={<RuxsatYoq />} />
{/* Barcha login qilganlar uchun */}
<Route element={<ProtectedRoute />}>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profil" element={<Profil />} />
</Route>
{/* Faqat adminlar uchun */}
<Route element={<ProtectedRoute kerakliRol="admin" />}>
<Route path="/admin" element={<AdminPanel />} />
<Route path="/admin/foydalanuvchilar" element={<FoydalanuvchilarBoshqaruv />} />
</Route>
<Route path="*" element={<TopilmadI />} />
</Routes>
Yuklanish holati — loading
Sahifa birinchi yuklanayotganda foydalanuvchi holati aniqlanmagan
bo'lishi mumkin (masalan, token tekshirilmoqda). Bu vaqtda
<Navigate> chaqirilsa — noto'g'ri yo'naltirish bo'ladi.
// components/ProtectedRoute.jsx
import { Navigate, Outlet, useLocation } from 'react-router-dom';
import { useAuth } from '../hooks/useAuth';
function ProtectedRoute() {
const { foydalanuvchi, yuklanmoqda } = useAuth();
const location = useLocation();
if (yuklanmoqda) {
return <div>Yuklanmoqda...</div>; // ← token tekshirilayotganda kuting
}
if (!foydalanuvchi) {
return <Navigate to="/kirish" state={{ from: location.pathname }} replace />;
}
return <Outlet />;
}
// hooks/useAuth.js — yuklanmoqda holati bilan
export function AuthProvider({ children }) {
const [foydalanuvchi, setFoydalanuvchi] = useState(null);
const [yuklanmoqda, setYuklanmoqda] = useState(true); // ← boshida true
useEffect(() => {
// Token tekshirish — server so'rovi
tokenniTekshir()
.then((mal) => setFoydalanuvchi(mal))
.catch(() => setFoydalanuvchi(null))
.finally(() => setYuklanmoqda(false)); // ← tekshiruv tugadi
}, []);
return (
<AuthContext.Provider value={{ foydalanuvchi, yuklanmoqda }}>
{children}
</AuthContext.Provider>
);
}
Tez-tez uchraydigan xatolar
1. replace ni unutish
// ❌ Xato — tarixga qo'shiladi
<Navigate to="/kirish" />
// ✅ To'g'ri — tarix almashtiriladi
<Navigate to="/kirish" replace />
Sababi: replace siz foydalanuvchi login qilgandan keyin
"orqaga" bossa — himoyalangan sahifaga qaytib, yana /kirish ga
yo'naltiriladi. Bu cheksiz aylanaga olib keladi. replace bilan
login sahifasi tarixdan o'chirilib, bunday muammo bo'lmaydi.
2. Yuklanish holatini hisobga olmaslik
// ❌ Xato — yuklanmoqda vaqtida foydalanuvchi null
function ProtectedRoute() {
const { foydalanuvchi } = useAuth();
// yuklanmoqda paytida foydalanuvchi === null
// Shuning uchun darhol /kirish ga yo'naltiriladi — noto'g'ri!
if (!foydalanuvchi) {
return <Navigate to="/kirish" replace />;
}
return <Outlet />;
}
// ✅ To'g'ri — yuklanmoqda tekshiriladi
function ProtectedRoute() {
const { foydalanuvchi, yuklanmoqda } = useAuth();
if (yuklanmoqda) return <div>Yuklanmoqda...</div>;
if (!foydalanuvchi) return <Navigate to="/kirish" replace />;
return <Outlet />;
}
Sababi: Token tekshiruvi asenkron. Sahifa birinchi yuklanganda
foydalanuvchi hali null — tekshiruv tugamagan. Bu paytda
<Navigate> chaqirilsa, login qilgan foydalanuvchi ham /kirish
ga yo'naltiriladi.
3. Har sahifaga alohida tekshiruv yozish
// ❌ Xato — har sahifada takrorlanmoqda
function Dashboard() {
const { foydalanuvchi } = useAuth();
if (!foydalanuvchi) return <Navigate to="/kirish" replace />;
return <div>Dashboard</div>;
}
function Profil() {
const { foydalanuvchi } = useAuth();
if (!foydalanuvchi) return <Navigate to="/kirish" replace />;
return <div>Profil</div>;
}
// ✅ To'g'ri — bir joyda ProtectedRoute
<Route element={<ProtectedRoute />}>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profil" element={<Profil />} />
</Route>
Sababi: Tekshiruvni har sahifada yozish — kod takrorlanadi
va xavfli. Biror sahifada unutib qolish mumkin. Markaziy
ProtectedRoute bilan — bir joyda o'zgartirsangiz hamma joyga ta'sir qiladi.
Xulosa
- ProtectedRoute — login holatini tekshiruvchi wrapper komponent
- Login qilinmagan →
<Navigate to="/kirish" replace /> - Login qilingan →
<Outlet />(bola Route ko'rsatiladi) state={{ from: location.pathname }}— qaytish manzilini saqlashyuklanmoqdaholatini tekshirish — token asenkron tekshirilganda- Rol asosida himoya —
kerakliRolprop bilan kengaytirish - Tekshiruvni har sahifada emas, markaziy
ProtectedRouteda yozing
Keyingi dars: 47-dars: Lazy Loading — Route larni bo'laklash, Suspense bilan