React Router Complete Guide | Client-Side Routing for React

React Router Complete Guide | Client-Side Routing for React

이 글의 핵심

React Router is the standard routing library for React applications. It enables navigation between views, URL parameter handling, and data loading for single-page applications.

Introduction

React Router is the standard routing library for React. It enables client-side routing, allowing users to navigate between different views without full page reloads.

Without React Router

function App() {
  const [page, setPage] = useState('home');
  
  return (
    <div>
      <button onClick={() => setPage('home')}>Home</button>
      <button onClick={() => setPage('about')}>About</button>
      
      {page === 'home' && <Home />}
      {page === 'about' && <About />}
    </div>
  );
}

Problems:

  • ❌ No URL updates
  • ❌ No browser back/forward
  • ❌ No bookmarking
  • ❌ No deep linking

With React Router

import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';

function App() {
  return (
    <BrowserRouter>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
      </nav>
      
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </BrowserRouter>
  );
}

1. Installation

npm install react-router-dom

2. Basic Routing

import { BrowserRouter, Routes, Route } from 'react-router-dom';

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/contact" element={<Contact />} />
        <Route path="*" element={<NotFound />} />
      </Routes>
    </BrowserRouter>
  );
}

3. Navigation

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

function Nav() {
  return (
    <nav>
      <Link to="/">Home</Link>
      <Link to="/about">About</Link>
      <Link to="/users/123">User 123</Link>
    </nav>
  );
}
import { NavLink } from 'react-router-dom';

function Nav() {
  return (
    <nav>
      <NavLink
        to="/"
        className={({ isActive }) => isActive ? 'active' : ''}
      >
        Home
      </NavLink>
      
      <NavLink
        to="/about"
        style={({ isActive }) => ({
          color: isActive ? 'red' : 'black'
        })}
      >
        About
      </NavLink>
    </nav>
  );
}

Programmatic Navigation

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

function LoginForm() {
  const navigate = useNavigate();
  
  const handleSubmit = async (e) => {
    e.preventDefault();
    await login();
    navigate('/dashboard'); // Navigate after login
  };
  
  return <form onSubmit={handleSubmit}>...</form>;
}

4. URL Parameters

Dynamic Routes

<Routes>
  <Route path="/users/:id" element={<UserProfile />} />
  <Route path="/posts/:year/:month/:slug" element={<Post />} />
</Routes>

Reading Parameters

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

function UserProfile() {
  const { id } = useParams();
  
  return <div>User ID: {id}</div>;
}

Query Strings

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

function SearchPage() {
  const [searchParams, setSearchParams] = useSearchParams();
  
  const query = searchParams.get('q');
  const page = searchParams.get('page') || '1';
  
  const handleSearch = (newQuery) => {
    setSearchParams({ q: newQuery, page: '1' });
  };
  
  return (
    <div>
      <p>Searching for: {query}</p>
      <p>Page: {page}</p>
    </div>
  );
}

5. Nested Routes

<Routes>
  <Route path="/" element={<Layout />}>
    <Route index element={<Home />} />
    <Route path="about" element={<About />} />
    <Route path="users" element={<Users />}>
      <Route index element={<UsersList />} />
      <Route path=":id" element={<UserProfile />} />
      <Route path=":id/settings" element={<UserSettings />} />
    </Route>
  </Route>
</Routes>
import { Outlet } from 'react-router-dom';

function Layout() {
  return (
    <div>
      <header>Header</header>
      <main>
        <Outlet /> {/* Child routes render here */}
      </main>
      <footer>Footer</footer>
    </div>
  );
}

6. Data Loading

Loaders (React Router 6.4+)

import { createBrowserRouter, RouterProvider } from 'react-router-dom';

const router = createBrowserRouter([
  {
    path: '/users/:id',
    element: <UserProfile />,
    loader: async ({ params }) => {
      const res = await fetch(`/api/users/${params.id}`);
      return res.json();
    },
  },
]);

function App() {
  return <RouterProvider router={router} />;
}
import { useLoaderData } from 'react-router-dom';

function UserProfile() {
  const user = useLoaderData();
  
  return <div>{user.name}</div>;
}

7. Form Actions

const router = createBrowserRouter([
  {
    path: '/users/:id',
    element: <EditUser />,
    loader: async ({ params }) => {
      const res = await fetch(`/api/users/${params.id}`);
      return res.json();
    },
    action: async ({ request, params }) => {
      const formData = await request.formData();
      const res = await fetch(`/api/users/${params.id}`, {
        method: 'PATCH',
        body: JSON.stringify(Object.fromEntries(formData)),
      });
      return res.json();
    },
  },
]);
import { Form, useLoaderData } from 'react-router-dom';

function EditUser() {
  const user = useLoaderData();
  
  return (
    <Form method="post">
      <input name="name" defaultValue={user.name} />
      <input name="email" defaultValue={user.email} />
      <button type="submit">Save</button>
    </Form>
  );
}

8. Error Handling

const router = createBrowserRouter([
  {
    path: '/',
    element: <Root />,
    errorElement: <ErrorPage />,
    children: [
      {
        path: 'users/:id',
        element: <UserProfile />,
        loader: async ({ params }) => {
          const res = await fetch(`/api/users/${params.id}`);
          if (!res.ok) throw new Response('Not Found', { status: 404 });
          return res.json();
        },
      },
    ],
  },
]);
import { useRouteError, isRouteErrorResponse } from 'react-router-dom';

function ErrorPage() {
  const error = useRouteError();
  
  if (isRouteErrorResponse(error)) {
    return (
      <div>
        <h1>{error.status} {error.statusText}</h1>
        <p>{error.data}</p>
      </div>
    );
  }
  
  return <div>Something went wrong!</div>;
}

9. Protected Routes

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

function ProtectedRoute({ children }) {
  const { user } = useAuth();
  
  if (!user) {
    return <Navigate to="/login" replace />;
  }
  
  return children;
}

// Usage
<Route
  path="/dashboard"
  element={
    <ProtectedRoute>
      <Dashboard />
    </ProtectedRoute>
  }
/>

10. TypeScript Integration

import { useParams, useLoaderData } from 'react-router-dom';

interface User {
  id: number;
  name: string;
  email: string;
}

function UserProfile() {
  const { id } = useParams<{ id: string }>();
  const user = useLoaderData() as User;
  
  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}

Summary

React Router enables powerful client-side routing:

  • Declarative routing with JSX
  • Nested routes for layouts
  • Data loading with loaders
  • Form handling with actions
  • TypeScript support

Key Takeaways:

  1. Use <Link> for navigation
  2. useParams for URL parameters
  3. Loaders for data fetching
  4. Actions for mutations
  5. Nested routes for layouts

Next Steps:

Resources: