Routing trong React JS - Phần 1
Bài đăng này đã không được cập nhật trong 5 năm
Trong những website thông thường khi điều hướng từ page này sang page khác, chúng ta sẽ sử dụng thẻ <a>
để làm điều đó, nhưng trong React JS thường được sử dụng để xây dựng nhữmg Single Page Application (SPA) khi chúng ta làm như vậy thì sẽ reloading lại toàn bộ page từ server, điều đó là không hiệu quả đối với 1 SPA. Thực chất SPA chỉ có 1 html page, nhưng bao gồm nhiều page views (components), và sẽ load ra page view tương ứng dựa trên route chứa component đó.
Và như vậy khái niệm routing ra đời dung để điều hướng từ page này sang page khác trong SPA mà không cần refresh browser. Trong bài này chúng ta sẽ giới thiệu đến 1 thư viện routing phổ biến và mạnh mẽ đó là React Router
Bắt đầu
Chúng ta cần tạo 1 React project dùng để demo sử dụng thư viện này
create-react-app react-router-demo
React Router
là thư viện thứ 3 dùng để điều hướng trong React app, nó xây dựng trên browser history API dùng để render ra component UI đồng bộ với URL của browser, nó gồm 3 packages sau:
react-router
react-router-dom
react-router-native
Mỗi package sẽ có 1 cách sử dụng khác nhau, react-router
là thành phần core để sử dụng cho react-router-dom
và react-router-native
. react-router-dom
sử dụng khi xây dựng web app; react-router-native
dùng khi xây dựng React Native(mobile) app. Như vậy chúng ta sẽ sử dụng package react-router-dom
trong bài viết này.
Add react-router-dom
vào project:
npm install react-router-dom
Sau khi install xong thì chúng ta run app lên bằng command npm start
, default là màn hình home http://localhost:3000/
Chúng ta sẽ tìm hiểu 3 thành phần chính của React router là BrowserRouter
, Route
và Link
BrowserRouter
: dùng để wrap tất cả route component
Route
: dùng để ẩn/hiện component (page) mà nó chứa
Link
: generate các link để di chuyển tới page mà ta mong muốn
BrowserRouter
Đây là một ví dụ đơn giản về BrowserRouter
. Import nó từ react-router-dom
và sử dụng nó để wrap tất cả app của bạn
import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter as Router } from 'react-router-dom'
ReactDOM.render(
<Router>
<div>
<!-- -->
</div>
</Router>,
document.getElementById('app')
)
BrowserRouter
chỉ có thể chứa 1 child component, vì vậy ta có thể wrap trong 1 thẻ div
hoặc react fragment <>
Route
Để tạo 1 route, import nó từ react-router-dom
import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter as Router, Route } from 'react-router-dom'
const Home = () => (
<div>
<h1>Home Component</h1>
</div>
)
const About = () => (
<div>
<h1>About Component</h1>
</div>
)
ReactDOM.render(
<Router>
<div>
<Route path='/' component={Home} />
<Route path='/about' component={About} />
</div>
</Router>,
document.getElementById('app')
)
Route
dùng để xác định component nào sẽ render ra tương ứng với path
nào, BrowserRouter
sẽ accept request URL của browser và sẽ match nó mới path
của Route
để render ra component mà route đó chứa, nếu không match với bất kì path
nào của Route
thì sẽ render ra null
.
Ở ví dụ trên access http://localhost:3000/
sẽ render ra component là Home
, nếu http://localhost:3000/about
thì render ra cả 2 component là Home
và About
, nếu chúng ta muốn chỉ render ra component mà match chính xác với URL thì thêm exact
property vào Route
, như vậy sẽ chỉ render ra component mà match chính xác với path
của Route
.
Có 3 cách render sử dung Route
:
<Route component>
: Render ra component chỉ khi location match với path prop.
<Route path='/' component={Home} />
<Route render>
: Giống như cách trên, nhưng cách này có thể render ra inline component và có thể pass thêm các props khác cho component.
const FadingRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={routeProps => (
<FadeIn>
<Component {...routeProps}/>
</FadeIn>
)}/>
)
<FadingRoute path="/cool" component={Something}/>
<Route children>
: Cách này sẽ luôn luôn render ra component, nhưng match props sẽ null nếu path không match với URL, vd: có thể dùng trong trường hợp để active menu bar item.
const ListItemLink = ({ to, ...rest }) => (
<Route
path={to}
children={({ match }) => (
<li className={match ? "active" : ""}>
<Link to={to} {...rest} />
</li>
)}
/>
);
<ul>
<ListItemLink to="/somewhere" />
<ListItemLink to="/somewhere-else" />
</ul>;
Link
Để điều hướng giữa các trang, chúng ta sử dụng thẻ <a>
. Tuy nhiên theo cách này sẽ refresh lại browser, như vậy sẽ không phù hợp đối với 1 SPA. Để tránh khỏi vấn đề đó, React Router
cung cấp
1 component Link
dùng để di chuyển qua lại giữa các page (component) mà không cần refresh browser.
import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter as Router, Route, Link } from 'react-router-dom'
ReactDOM.render(
<Router>
<div>
<nav>
<Link to='/'>Home</Link>
<Link to='/about'>About</Link>
</nav>
<Route path='/' component={Home} />
<Route path='/about' component={About} />
</div>
</Router>,
document.getElementById('app')
)
Như ví dụ trên, mỗi Link
component sẽ render ra 1 thẻ <a>
trên HTML page, và sẽ di chuyển đến Route
có path
tương ứng với to
props của Link
đó.
Có 2 cách để define Link
như sau:
-
<Link to='path-name'>
: di chuyển đếnRoute
mà không cần thêm bất kỳ state, hay search params nào cả. -
<Link to={{pathname: 'abc', state: { title: 'this is title' }}}>
: di chuyển đến Route và có thêm state đi kèm.
Mỗi lần click vào Link
thì browser sẽ add 1 entry lưu lại state, params vào trong stack history, nếu back lại thì sẽ remove entry đó khỏi history.
Dynamic Routing
Ở phần này, chúng ta sẽ tạo và sử dụng dynamic routes dựa trên query params như là :id
.
Kịch bản như sau: route /posts
sẽ display danh sách title các posts, route posts/:id
sẽ display id của mỗi post đó.
function Child({ match }) {
return (
<div>
<h3>ID: {match.params.id}</h3>
</div>
)
}
class Posts extends React.Component {
state = {
posts: [
{
id: 1,
title: "Hello Blog World!"
},
{
id: 2,
title: "My second post"
},
{
id: 3,
title: "What is React Router?"
}
]
}
render() {
const { posts } = this.state
return (
<div className='posts'>
<h1>Posts List</h1>
<ul>
{posts.map(post => (
<li key={post.id}>
<Link to={`/posts/${post.id}`}>{post.title}</Link>
</li>
))}
</ul>
<Route path='/posts/:id' component={Child} />
</div>
)
}
}
ReactDOM.render(
<Router>
<div>
<nav>
<Link to='/' exact>Home</Link>
<Link to='/about' exact>About</Link>
<Link to='/posts' exact>Posts</Link>
</nav>
<Route path='/' exact component={Home} />
<Route path='/about' component={About} />
<Route path='/posts' component={Posts} />
</div>
</Router>,
document.getElementById('app')
)
Ở ví dụ trên, ta sẽ get được match
props của Child
component, match
props là một object có thể truy cập được các thông tin mà route cung cấp như params
, isExact
, path
, url
.
Có thể tham khảo official docs để hiểu thêm nhé https://reacttraining.com/react-router/web/api/match
Kết luận
Bây giờ bạn đã làm quen với routing trong react và thư viện React Router
, hy vọng bạn sẽ sử dụng nó để buid 1 react app tốt hơn nhé, mình sẽ giới thiệu đến các thành phần còn lại của React Router
như Redirect
, Switch
, history
api, ... ở phần 2 nhé. Cảm ơn các bạn.
Refs:
All rights reserved