Jak powstał Destinations (cz. 4): chronione trasy w React Router

14.10.2026 · 2 min czytania

W części 3 dodałem logowanie i wiem już, kto jest zalogowany (dzięki AuthContext). Zostało pytanie: jak fizycznie nie wpuścić niezalogowanych do panelu szkoleniowca?

Ukrycie linku to nie zabezpieczenie#

Pierwsza myśl: „schowam przycisk do panelu, jak ktoś nie jest zalogowany”. Tylko że to żadna ochrona.

Potrzebowałem czegoś, co naprawdę pilnuje trasy — sprawdza, czy jesteś zalogowany, zanim w ogóle pokaże panel. A jeśli nie jesteś, odsyła Cię do logowania.

ProtectedRoute — strażnik trasy#

W React Router zrobiłem komponent ProtectedRoute, który opakowuje chronione trasy. Układ tras wygląda tak:

<Route
  path="/panel"
  element={
    <ProtectedRoute>
      <Panel />
    </ProtectedRoute>
  }
/>

Generator i logowanie są publiczne, a /admin siedzi wewnątrz ProtectedRoute — czyli przechodzi przez strażnika. Sam strażnik:

import { Navigate } from "react-router-dom";
import { useAuth } from "../contexts/useAuth";
import type { ReactNode } from "react";

const ProtectedRoute = ({ children }: { children: ReactNode }) => {
  const { currentUser } = useAuth();

  if (!currentUser) {
    return <Navigate to="/login" replace />;
  }

  return children;
};

export default ProtectedRoute;

Pułapka, w którą można wpaść: stan ładowania#

Lekarstwo to dołożenie stanu loading w AuthContext (ustawianego w onAuthStateChanged) i wstrzymanie decyzji, dopóki Firebase nie skończy:

const ProtectedRoute = ({ children }: { children: ReactNode }) => {
  const { currentUser, loading } = useAuth();

  if (loading) {
    return null; // albo <Spinner />
  }

  if (!currentUser) {
    return <Navigate to="/login" replace />;
  }

  return children;
};

Dopóki loading jest true, nic nie renderujemy (albo pokazujemy spinner) — więc nie ma jak omyłkowo przekierować. To zostawiam sobie na kolejny krok.

Najważniejsze: to wciąż tylko połowa#

I tu wracam do lekcji z części 3. ProtectedRoute jest ważny, ale nie jest prawdziwym zabezpieczeniem danych.

Dopiero obie warstwy razem dają sensowne bezpieczeństwo: jedna dba o to, co widać, druga o to, co można zrobić z danymi.

W części 5 — finał serii: dlaczego cała aplikacja powstała, czyli generowanie testów i tabel do druku (i CSS, o którym mało kto myśli: @media print).

Więcej z tej serii

  1. 1.Jak powstał Destinations (cz. 1): pomysł, domena i stack
  2. 2.Jak powstał Destinations (cz. 2): z JavaScriptu na TypeScript
  3. 3.Jak powstał Destinations (cz. 3): Firebase — baza i logowanie
  4. 4.Jak powstał Destinations (cz. 4): chronione trasy w React Router
  5. 5.Jak powstał Destinations (cz. 5): generatory testów i CSS do druku