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
Link Component
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>
);
}
NavLink (Active Styling)
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:
- Use
<Link>for navigation useParamsfor URL parameters- Loaders for data fetching
- Actions for mutations
- Nested routes for layouts
Next Steps:
Resources: