React router V6 xuất hiện sử dụng các tính năng tốt nhất từ các phiên bản trước đã kết thúc một thập kỉ định tuyến phía máy khách, nó tương thích với react từ 16.8 trở lên. Bài viết này mình sẽ tổng hợp những kiến thức cơ bản đến nâng cao về react-router-dom
, các ví dụ được viết và chỉnh sửa trên codesandbox để dễ theo dõi.
1. Cài đặt
# Tạo một react app
npx create-react-app router-tutorial
# Cài dependencies
cd router-tutorial
npm install [email protected]
Thêm bootstrap v5 vào file public/index.html
2. Giao diện và cấu trúc component
- Giao diện
- Cấu trúc component
- App
|-- Sidebar
|-- Content
3. Configuring Routes
Đầu tiên muốn kết nối ứng dụng của bạn với URL của trình duyệt thì phải import BrowserRouter
và bọc nó bên ngoài component chính của toàn bộ ứng dụng chính là component App
import ReactDOM from'react-dom'import App from'./App'import{ BrowserRouter }from'react-router-dom'const rootElement = document.getElementById('root')
ReactDOM.render(<BrowserRouter><App/></BrowserRouter>,
rootElement
)
Tại component Sidebar
ở giao diện ban đầu chúng ta mới chỉ sử dụng thẻ a, giờ sẽ chuyển những thẻ đó sang thẻ điều hướng <Link>
import{ Link }from'react-router-dom'exportconstSidebar=()=>{return(<ul><li><Linkto="/"className="nav-link">Dashboard</Link></li><li><Linkto="/orders"className="nav-link">Orders</Link></li><li><Linkto="/products"className="nav-link">Products</Link></li><li><Linkto="/customers"className="nav-link">Customers</Link></li></ul>)}
Như vậy tại Sidebar
chúng ta đã điều hướng sang các URL khác nhau, giờ với các URL đó sẽ load các component tương ứng, tại component App
sửa lại
import{ Routes, Route }from'react-router-dom'exportdefaultfunctionApp(){return(<Routes><Routepath="/"element={<Dashboard/>}/><Routepath="/orders"element={<Orders/>}/><Routepath="/products"element={<Products/>}/><Routepath="/customers"element={<Customers/>}/></Routes>)}
Code demo: https://codesandbox.io/s/configuring-routes-c87nm
4. Active Link
Ở phần trước tại component Sidebar
khi chúng ta sử dụng <Link>
thì output thực tế ra HTML như này
<LinkclassName="nav-link"to="/orders">Orders</Link>=== html ===<aclass="nav-link"href="/orders">Orders</a>
Vẫn khá là ok nhưng giờ chúng ta muốn có thêm class active
hoặc style gì đó đặc biệt cho link đang xem thì sẽ không dùng cách này được.
4-1. Active link với tên class là active
Nếu class tên là active
thì khá là đơn giản, nhẹ nhàng chúng ta chỉ việc thay <Link>
thành <NavLink>
các bạn sẽ thấy việc output ra html cũng đã khác nhau rồi.
<NavLinkclassName="nav-link"to="/orders">Orders</NavLink>=== html ===<aaria-current="page"class="nav-link active"href="/orders">Orders</a>
Code demo: https://codesandbox.io/s/active-link-class-active-ubh4h?file=/src/components/Sidebar.js
4-2. Active link với tên class khác
Nếu chúng ta muốn tên class khi được active khác đi chẳng hạn như activated hay current-page thì viết kiểu function như này
import{ NavLink }from'react-router-dom'exportconstSidebar=()=>{constnavLinkClass=({ isActive })=>{return isActive ?"nav-link activated":"nav-link"}return(<ul><li><NavLinkto="/"className={navLinkClass}>Dashboard</NavLink></li></ul>)}
Code demo: https://codesandbox.io/s/active-link-class-activated-crjtu?file=/src/components/Sidebar.js
4-3. Active link với style
Nếu không muốn trạng thái active viết theo kiểu class mà viết kiểu style thì làm như này
import{ NavLink }from'react-router-dom'exportconstSidebar=()=>{constnavLinkStyle=({ isActive })=>({
color: isActive ?"#fff":"",
backgroundColor: isActive ?"#0d6efd":""})return(<ul><li><NavLinkto="/"className="nav-link"style={navLinkStyle}>Dashboard</NavLink></li></ul>)}
Code demo: https://codesandbox.io/s/active-link-style-xbyei?file=/src/components/Sidebar.js
4-4. Custom active link
Nhiều khi đời không như mơ, vì vài lí do nào đó mà chúng ta không thể đặt class active vào chính thẻ a mà phải đặt ở thẻ cha của nó như thẻ li chẳng hạn
<liclass="active"><ahref="/">Dashboard</a></li>
Ví dụ sau trình bày cách tạo một Custom Active Link sử dụng hook useMatch()
và useResolvedPath()
import{ Link, useMatch, useResolvedPath }from'react-router-dom'constSidebar=()=>{constCustomLink=({ children, to,...props })=>{let resolved =useResolvedPath(to)let match =useMatch({ path: resolved.pathname, end:true})return(<liclassName={match ?"nav-item active":"nav-item"}><LinkclassName="nav-link"to={to}{...props}>{children}</Link></li>)}return(<ulclassName="nav nav-pills flex-column"><CustomLinkto="/">Dashboard</CustomLink><CustomLinkto="/orders">Orders</CustomLink><CustomLinkto="/products">Products</CustomLink><CustomLinkto="/customers">Customers</CustomLink></ul>)}exportdefault Sidebar
Code demo: https://codesandbox.io/s/custom-active-link-83m1q?file=/src/components/Sidebar.js
5. Navigate programmatically
Ở các phần ví dụ trên khi người dùng click vào các link ở Sidebar
thì chuyển trang được rồi nhưng nếu trường hợp chúng ta muốn chuyển trang tự động thì sao? Ví dụ chúng ta có một form đăng nhập khi login thành công thì chuyển hướng họ đến một trang nào đó. Để làm được yêu cầu này chúng ta sử dụng đến hook useNavigate
5-1. useNavigate với 1 tham số
Ví dụ bên dưới khi click vào button thì sẽ chuyển đến trang orders
import{ useNavigate }from'react-router-dom'exportconstDashboard=()=>{const navigate =useNavigate()return(<buttononClick={()=>navigate('orders')}>
Go to Orders
</button>)}
Code demo: https://codesandbox.io/s/use-navigate-1-f512r?file=/src/components/Dashboard.js
5-2. useNavigate với history
Trường hợp bạn muốn sử dụng go
, goBack
, goForward
trong lịch sử trình duyệt.
import{ useNavigate }from'react-router-dom'exportdefaultfunctionApp(){const navigate =useNavigate()return(<><buttononClick={()=>navigate(-2)}>Go 2 pages back</button><buttononClick={()=>navigate(-1)}>Go back</button><buttononClick={()=>navigate(1)}>Go forward</button><buttononClick={()=>navigate(2)}>Go 2 pages forward</button></>)}
Code demo: https://codesandbox.io/s/use-navigate-2-885g1?file=/src/components/Orders.js
5-3. useNavigate với replace true
Sử dụng tham số thứ hai của navigate
để chỉ thay đổi URL chứ không muốn URL đó lưu lại trong lịch sử trình duyệt. Kiểu như tại trang A đi tới trang B, tại trang B chúng ta click back trên trình duyệt thì sẽ không quay trở lại trang A nữa.
import{ useNavigate }from'react-router-dom'exportconstDashboard=()=>{const navigate =useNavigate()return(<buttononClick={()=>navigate('orders',{replace:true})}>
Go to Orders
</button>)}
Code demo: https://codesandbox.io/s/use-navigate-3-sb4oi?file=/src/components/Dashboard.js
6. Not Found Routes – 404
Trường hợp này xảy ra nếu người dùng nhập vào một URL không hợp lệ như localhost:3000/abcdef
thì chúng ta sẽ điều hướng tới trang 404. Tốt nhất chúng ta nên để Not Found Routes
này ở dưới cùng để nếu các route ở trên không cái nào trùng khớp thì sẽ đến route này.
<Routes><Routepath="/"element={<Dashboard/>}/><Routepath="*"element={<NotFound/>}/></Routes>
Code demo: https://codesandbox.io/s/not-found-routes-404-y5gjs?file=/src/App.js
7. Nested Routes
Nested Routes để lồng component con vào trong component cha.
<Routes><Routepath="/"element={<Dashboard/>}/><Routepath="/products"element={<Products/>}><Routepath="laptop"element={<Laptop/>}/><Routepath="desktop"element={<Desktop/>}/></Route></Routes>
import{ Outlet }from'react-router-dom'exportdefaultfunctionProducts(){return(<><h1>Products</h1><Outlet/></>)}
Như code ở trên
- Nếu truy cập vào localhost:3000/products sẽ load component
Products
- Nếu truy cập vào localhost:3000/products/laptop sẽ load
Products
bên trong có componentLaptop
- Nếu truy cập vào localhost:3000/products/desktop sẽ load
Products
bên trong có componentDesktop
- Lưu ý tại component cha là
Products
ta phải sử dụng<Outlet />
để hiển thị vị trí component con khi route trùng khớp - Code demo: https://codesandbox.io/s/nested-routes-ub8ee?file=/src/App.js
8. Index routes
- Như ví dụ ở trên thì khi truy cập vào localhost:3000/products sẽ load component
Products
và không hiển thị conponent con nào cả - Điều chúng ta mong muốn là vẫn URL như vậy nhưng hiển thị một conponent con ở ngay component cha
- Để làm được điều đó ta sử dụng thêm index và truyền conponent con muốn được hiển thị
<Routes><Routepath="/products"element={<Products/>}><Routeindexelement={<BestSeller/>}/><Routepath="laptop"element={<Laptop/>}/><Routepath="desktop"element={<Desktop/>}/></Route></Routes>
9. Dynamic routes
<Routes><Routepath="/"element={<Dashboard/>}/><Routepath="/courses"element={<Courses/>}/><Routepath="/courses/:courseId"element={<CourseDetail/>}/></Routes>
import{ useParams }from'react-router-dom'exportconstCustomerDetail=()=>{const params =useParams()return<h2>Chi tiết khóa học: {params.courseId}</h2>}
- Dynamic routes giúp chúng ta giải quyết các routes động, routes thay đổi theo một cấu trúc đã được định nghĩa sẵn.
- Ví dụ ta có URL theo kiểu
courses/html
,courses/css
,courses/javascript
thì có thể mô hình hóa nó thànhcourses/:courseId
- Tại
CourseDetail
ta sử dụnguseParams
để lấy tham số trên URL. - Nếu ta định nghĩa route là
courses/:courseId
thì lúc lấy giá trị cũng lấy đúng tên làparams.courseId
- Code demo: https://codesandbox.io/s/dynamic-routes-1iedr?file=/src/App.js
9-1. Dynamic routes với các route cố định
<Routes><Routepath="/courses"element={<Courses/>}/><Routepath="/courses/:courseId"element={<CourseDetail/>}/><Routepath="/courses/add-course"element={<AddCourse/>}/><Routepath="/courses/edit-course"element={<EditCourse/>}/></Routes>
- Khi sử dụng Dynamic routes ta có URL theo kiểu
courses/html
,courses/css
,courses/javascript
thì đã mô hình hóa nó thànhcourses/:courseId
- Trong một vài trường hợp route cố định như
courses/add-course
haycourses/edit-course
thì ta sẽ khai báo route để bắt các trường hợp này - Code demo: https://codesandbox.io/s/dynamic-routes-2-zb3h2?file=/src/App.js
9-2. Multiple dynamic routes
<Routes><Routepath="/courses"element={<Courses/>}/><Routepath="/courses/:courseType/"element={<CourseType/>}/><Routepath="/courses/:courseType/:courseId"element={<CourseDetail/>}/></Routes>
- Đoạn code trên áp dụng với trường hợp nhiều URL Parameters
- Code demo: https://codesandbox.io/s/dynamic-routes-3-1dzf2?file=/src/App.js
10. Search params
Đang cập nhật
11. Relative link
Đang cập nhật
12. Protected routes
Đang cập nhật
13. Lazy loading
Đang cập nhật
Nguồn: viblo.asia