React & Next.js Topic
React API Integration & Data Fetching
1. React Router Basics
Installation and Setup
bash
npm install react-router-dom
Basic Router Setup
jsx
import React from 'react';
import {
BrowserRouter as Router,
Routes,
Route,
Link
} from 'react-router-dom';
function App() {
return (
<Router>
<div className="app">
{/* Navigation */}
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/users">Users</Link>
<Link to="/contact">Contact</Link>
</nav>
{/* Route Configuration */}
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/users" element={<Users />} />
<Route path="/contact" element={<Contact />} />
<Route path="*" element={<NotFound />} />
</Routes>
</div>
</Router>
);
}
// Basic Page Components
function Home() {
return (
<div>
<h1>Home Page</h1>
<p>Welcome to our website!</p>
</div>
);
}
function About() {
return (
<div>
<h1>About Us</h1>
<p>Learn more about our company.</p>
</div>
);
}
function Users() {
return (
<div>
<h1>Users</h1>
<p>User management page.</p>
</div>
);
}
function Contact() {
return (
<div>
<h1>Contact</h1>
<p>Get in touch with us.</p>
</div>
);
}
function NotFound() {
return (
<div>
<h1>404 - Page Not Found</h1>
<p>The page you're looking for doesn't exist.</p>
</div>
);
}
2. Dynamic Routes and Parameters
URL Parameters
jsx
import { Routes, Route, useParams, Link } from 'react-router-dom';
function App() {
return (
<Router>
<Routes>
<Route path="/users" element={<UserList />} />
<Route path="/users/:userId" element={<UserProfile />} />
<Route path="/products/:category/:id" element={<ProductDetail />} />
</Routes>
</Router>
);
}
function UserList() {
const users = [
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Smith' },
{ id: 3, name: 'Bob Johnson' }
];
return (
<div>
<h1>User List</h1>
<ul>
{users.map(user => (
<li key={user.id}>
<Link to={`/users/${user.id}`}>
{user.name}
</Link>
</li>
))}
</ul>
</div>
);
}
function UserProfile() {
const { userId } = useParams();
return (
<div>
<h1>User Profile</h1>
<p>Viewing profile for user ID: {userId}</p>
<Link to="/users">Back to Users</Link>
</div>
);
}
function ProductDetail() {
const { category, id } = useParams();
return (
<div>
<h1>Product Details</h1>
<p>Category: {category}</p>
<p>Product ID: {id}</p>
</div>
);
}
Query Parameters
jsx
import { useSearchParams } from 'react-router-dom';
function SearchPage() {
const [searchParams, setSearchParams] = useSearchParams();
const query = searchParams.get('q') || '';
const category = searchParams.get('category') || 'all';
const handleSearch = (newQuery, newCategory) => {
setSearchParams({
q: newQuery,
category: newCategory
});
};
return (
<div>
<h1>Search Results</h1>
<p>Query: {query}</p>
<p>Category: {category}</p>
<button onClick={() => handleSearch('react', 'books')}>
Search React Books
</button>
</div>
);
}
3. Navigation Patterns
Programmatic Navigation
jsx
import { useNavigate } from 'react-router-dom';
function LoginForm() {
const navigate = useNavigate();
const [formData, setFormData] = useState({ email: '', password: '' });
const handleSubmit = async (e) => {
e.preventDefault();
// Simulate login API call
try {
await loginUser(formData);
// Redirect after successful login
navigate('/dashboard', {
replace: true,
state: { from: 'login' }
});
} catch (error) {
console.error('Login failed:', error);
}
};
const handleCancel = () => {
// Go back to previous page
navigate(-1);
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={formData.email}
onChange={(e) => setFormData({...formData, email: e.target.value})}
placeholder="Email"
/>
<input
type="password"
value={formData.password}
onChange={(e) => setFormData({...formData, password: e.target.value})}
placeholder="Password"
/>
<button type="submit">Login</button>
<button type="button" onClick={handleCancel}>Cancel</button>
</form>
);
}
// Navigation with state
function ProductList() {
const navigate = useNavigate();
const viewProduct = (product) => {
navigate(`/products/${product.id}`, {
state: {
product,
from: 'product-list',
search: 'electronics'
}
});
};
return (
<div>
{/* Product list with click handlers */}
</div>
);
}
function ProductDetail() {
const location = useLocation();
const product = location.state?.product;
if (!product) {
// Handle case where product is not in state (direct URL access)
return <div>Product not found</div>;
}
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
</div>
);
}
Active Navigation Links
jsx
import { NavLink, useLocation } from 'react-router-dom';
function Navigation() {
const location = useLocation();
return (
<nav className="main-nav">
<NavLink
to="/"
className={({ isActive }) =>
isActive ? 'nav-link active' : 'nav-link'
}
end
>
Home
</NavLink>
<NavLink
to="/about"
className={({ isActive }) =>
isActive ? 'nav-link active' : 'nav-link'
}
>
About
</NavLink>
<NavLink
to="/products"
className={({ isActive }) =>
isActive ? 'nav-link active' : 'nav-link'
}
>
Products
</NavLink>
{/* Custom active logic */}
<NavLink
to="/contact"
className={({ isActive }) =>
isActive || location.pathname.startsWith('/contact')
? 'nav-link active'
: 'nav-link'
}
>
Contact
</NavLink>
</nav>
);
}
4. Nested Routes
Basic Nested Routes
jsx
function App() {
return (
<Router>
<Routes>
<Route path="/dashboard" element={<Dashboard />}>
<Route index element={<DashboardHome />} />
<Route path="profile" element={<DashboardProfile />} />
<Route path="settings" element={<DashboardSettings />} />
<Route path="analytics" element={<DashboardAnalytics />} />
</Route>
</Routes>
</Router>
);
}
function Dashboard() {
return (
<div className="dashboard">
<DashboardSidebar />
<div className="dashboard-content">
<Outlet /> {/* This is where nested routes render */}
</div>
</div>
);
}
function DashboardSidebar() {
return (
<nav className="dashboard-sidebar">
<NavLink to="/dashboard" end>Overview</NavLink>
<NavLink to="/dashboard/profile">Profile</NavLink>
<NavLink to="/dashboard/settings">Settings</NavLink>
<NavLink to="/dashboard/analytics">Analytics</NavLink>
</nav>
);
}
function DashboardHome() {
return <h2>Dashboard Overview</h2>;
}
function DashboardProfile() {
return <h2>User Profile</h2>;
}
function DashboardSettings() {
return <h2>Settings</h2>;
}
function DashboardAnalytics() {
return <h2>Analytics</h2>;
}
Complex Nested Routes with Parameters
jsx
function App() {
return (
<Router>
<Routes>
<Route path="/admin" element={<AdminLayout />}>
<Route index element={<AdminDashboard />} />
<Route path="users" element={<UsersLayout />}>
<Route index element={<UserList />} />
<Route path=":userId" element={<UserDetail />}>
<Route index element={<UserProfile />} />
<Route path="edit" element={<EditUser />} />
<Route path="permissions" element={<UserPermissions />} />
</Route>
<Route path="new" element={<NewUser />} />
</Route>
<Route path="products/*" element={<ProductsManagement />} />
</Route>
</Routes>
</Router>
);
}
function AdminLayout() {
return (
<div className="admin-layout">
<AdminHeader />
<div className="admin-content">
<Outlet />
</div>
</div>
);
}
function UsersLayout() {
return (
<div className="users-layout">
<h2>User Management</h2>
<div className="users-content">
<Outlet />
</div>
</div>
);
}
function UserDetail() {
const { userId } = useParams();
return (
<div className="user-detail">
<h3>User ID: {userId}</h3>
<nav className="user-subnav">
<NavLink to={`/admin/users/${userId}`} end>Profile</NavLink>
<NavLink to={`/admin/users/${userId}/edit`}>Edit</NavLink>
<NavLink to={`/admin/users/${userId}/permissions`}>Permissions</NavLink>
</nav>
<div className="user-detail-content">
<Outlet />
</div>
</div>
);
}
5. Route Protection and Authentication
Protected Routes
jsx
import { Navigate, useLocation } from 'react-router-dom';
// Auth context (simplified)
const AuthContext = React.createContext();
function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Check for existing auth token
const token = localStorage.getItem('authToken');
if (token) {
// Validate token and set user
setUser({ name: 'John Doe', role: 'user' });
}
setLoading(false);
}, []);
const login = (userData) => {
setUser(userData);
localStorage.setItem('authToken', 'fake-token');
};
const logout = () => {
setUser(null);
localStorage.removeItem('authToken');
};
return (
<AuthContext.Provider value={{ user, login, logout, loading }}>
{children}
</AuthContext.Provider>
);
}
function useAuth() {
const context = React.useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within AuthProvider');
}
return context;
}
// Protected Route Component
function ProtectedRoute({ children, requiredRole }) {
const { user, loading } = useAuth();
const location = useLocation();
if (loading) {
return <div>Loading...</div>;
}
if (!user) {
// Redirect to login with return url
return <Navigate to="/login" state={{ from: location }} replace />;
}
if (requiredRole && user.role !== requiredRole) {
return <Navigate to="/unauthorized" replace />;
}
return children;
}
// Usage in App
function App() {
return (
<AuthProvider>
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/public" element={<PublicPage />} />
{/* Protected Routes */}
<Route
path="/dashboard"
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>
<Route
path="/admin"
element={
<ProtectedRoute requiredRole="admin">
<AdminPanel />
</ProtectedRoute>
}
/>
<Route path="/unauthorized" element={<Unauthorized />} />
</Routes>
</Router>
</AuthProvider>
);
}
6. Advanced Routing Patterns
Lazy Loading with Suspense
jsx
import { lazy, Suspense } from 'react';
const LazyHome = lazy(() => import('./pages/Home'));
const LazyAbout = lazy(() => import('./pages/About'));
const LazyContact = lazy(() => import('./pages/Contact'));
function App() {
return (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<LazyHome />} />
<Route path="/about" element={<LazyAbout />} />
<Route path="/contact" element={<LazyContact />} />
</Routes>
</Suspense>
</Router>
);
}
Route Configuration Object
jsx
// routes.js
export const routes = [
{
path: '/',
element: <Home />,
title: 'Home',
exact: true
},
{
path: '/about',
element: <About />,
title: 'About Us'
},
{
path: '/products',
element: <Products />,
title: 'Products',
children: [
{
path: ':category',
element: <ProductCategory />
},
{
path: ':category/:id',
element: <ProductDetail />
}
]
}
];
// App.js
import { routes } from './routes';
function renderRoutes(routes) {
return routes.map((route, index) => (
<Route
key={index}
path={route.path}
element={route.element}
exact={route.exact}
>
{route.children && renderRoutes(route.children)}
</Route>
));
}
function App() {
return (
<Router>
<Routes>
{renderRoutes(routes)}
</Routes>
</Router>
);
}
Custom Navigation Hook
jsx
import { useNavigate, useLocation } from 'react-router-dom';
function useNavigation() {
const navigate = useNavigate();
const location = useLocation();
const goTo = (path, options = {}) => {
navigate(path, options);
};
const goBack = () => {
navigate(-1);
};
const goForward = () => {
navigate(1);
};
const replace = (path, state) => {
navigate(path, { replace: true, state });
};
const getQueryParam = (param) => {
const searchParams = new URLSearchParams(location.search);
return searchParams.get(param);
};
const setQueryParam = (params) => {
const searchParams = new URLSearchParams(location.search);
Object.entries(params).forEach(([key, value]) => {
if (value === null || value === undefined) {
searchParams.delete(key);
} else {
searchParams.set(key, value);
}
});
navigate(`${location.pathname}?${searchParams.toString()}`, {
replace: true
});
};
return {
goTo,
goBack,
goForward,
replace,
getQueryParam,
setQueryParam,
currentPath: location.pathname,
state: location.state
};
}
// Usage
function MyComponent() {
const navigation = useNavigation();
const handleSearch = (query) => {
navigation.setQueryParam({ q: query, page: 1 });
};
return (
<div>
<button onClick={() => navigation.goTo('/dashboard')}>
Go to Dashboard
</button>
<button onClick={() => navigation.replace('/login')}>
Replace with Login
</button>
</div>
);
}
7. Error Boundaries with Routes
jsx
import { ErrorBoundary } from 'react-error-boundary';
function ErrorFallback({ error, resetErrorBoundary }) {
return (
<div role="alert">
<h2>Something went wrong:</h2>
<pre>{error.message}</pre>
<button onClick={resetErrorBoundary}>Try again</button>
</div>
);
}
function App() {
return (
<ErrorBoundary
FallbackComponent={ErrorFallback}
onReset={() => window.location.reload()}
>
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
{/* Routes with their own error boundaries */}
<Route
path="/dashboard/*"
element={
<ErrorBoundary fallback={<div>Dashboard Error</div>}>
<DashboardRoutes />
</ErrorBoundary>
}
/>
</Routes>
</Router>
</ErrorBoundary>
);
}
This comprehensive routing guide covers everything from basic setup to advanced patterns like nested routes, authentication, and performance optimization with lazy loading.
Comments (0)
No comments yet. Be the first to comment!