Tìm hiểu về react router v4

Sơ lược về react router v4

Những ai đã học qua về react js chắc hẳn sẽ biết rằng React.js chỉ là thư viện để tạo các Component và từ các Component đấy xây dựng thành view giao diện, đặc biệt ở đây là bản thân react js nó không có Router. Vì vậy, react router được sinh ra để giải quyết vấn đề này.
React-router được dùng để giúp việc dẫn hướng UI đồng bộ với URL. Để dễ hiểu hơn về react router, mình sẽ ví dụ như là bạn làm xong tất cả các chi tiết trong đồng hồ nhưng không có cách nào để lắp ghép các chi tiết để chiếc đồng hồ đó chạy được vậy. React-router chính là cách kết nối đó. Giúp các thành phần của react liên kết được với nhau.
Trong hướng dẫn này, mình sẽ xây dựng trang web. Tuy nhiên mình sẽ bỏ qua tất cả những điều cơ bản cần thiết để có được trang web và định tuyến lại chúng. Điều này sẽ bao gồm:

  • Chọn router.
  • Xây dựng các định tuyến.
  • Điều hướng giữa các tuyến sử dụng liên kết.

Source code đơn giản

Dưới đây là một đoạn mã code khá đơn giản để chạy là một trang cơ bản nhất. Trong phần giới thiệu này mình sẽ không chú trọng vào style cho trang web mà chỉ đưa ra các đoạn source đơn giản nhất nhằm mục đích demo về tính năng của react router

import React from 'react';
import { render } from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import App from './components/App';

render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root'),
);

Cài đặt

React Router về cơ bản đều giống nhau và chúng được sử dụng chia thành 3 package: react-router, react-router-dom and react-router-native. Nhưng hầu hết mọi người sẽ không cài đặt react-router một cách trực tiếp. Bởi vì chúng cung cấp các thành phần và chức năng định tuyến chính cho các ứng dụng React Router, tuy nhiên hai phần khác cung cấp các thành phần cụ thể về môi trường (trình duyệt và react-native), lại đều dựa theo các định hướng của react-router.
Trong 2 lựa chọn sau, tùy thuộc vào ứng dụng react của bạn mà bạn nên chọn 1 thứ. Ở đây, mình đang xây dựng 1 trang web nên mình sẽ cài đặt react-router-dom npm install --save react-router-dom

Bộ định tuyến

Khi bắt đầu một dự án mới, bạn cần phải xác định loại router sử dụng. Đối với các dự án dựa trên trình duyệt, có các thành phần <BrowserRouter> và <HashRouter>. <BrowserRouter> nên được sử dụng khi bạn có một máy chủ sẽ xử lý yêu cầu động (biết làm thế nào để đáp ứng với bất kỳ URL nào có thể), trong khi <HashRouter> nên được sử dụng cho các trang web tĩnh (chỉ có thể đáp ứng các yêu cầu cho các tập tin trong phạm vi nó biết). Thường thì tốt hơn là sử dụng một <BrowserRouter>, nhưng nếu trang web của bạn sẽ được lưu trữ trên một máy chủ chỉ phục vụ các tập tin tĩnh, thì <HashRouter> là một giải pháp tốt. Đối với project hôm nay, chúng ta sẽ giả định trang web của chúng ta được hỗ trợ bởi server động, và vì vậy, chúng ta sẽ sử dụng <BrowserRouter>

Rendering một router

Các Router COmponents chỉ mong đợi nhận được 1 phần tử con duy nhất. Để làm được điều này, sẽ rất hay nếu bạn tạo ra <App /> components để render ra các phần còn lại của ứng dụng

import { BrowserRouter } from 'react-router-dom'
ReactDOM.render((
  <BrowserRouter>
    <App />
  </BrowserRouter>
), document.getElementById('root'))

Trên đây, chúng ta đã chọn xong Router, giờ bắt đầu đến việc xây dựng app.

<App> Components

Ứng dụng của mình được định nghĩa trong thành phần <App>. Để đơn giản hóa mọi thứ, mình sẽ chia ứng dụng thành hai phần. Thành phần <Header> sẽ chứa liên kết để điều hướng trong toàn bộ trang web. Thành phần <Main> là nơi mà phần nội dung còn lại sẽ được hiển thị.

// this component will be rendered by our <___Router>
const App = () => (
 <div>
   <Header />
   <Main />
 </div>
)

Thành phần <Route> là components chính của React Router. Bất cứ nơi nào mà bạn muốn chỉ render ra một cái gì đó phù hợp đường dẫn, bạn nên tạo một phần tử <Route>. Một <Route> dự kiến một đường dẫn prop, nó mô tả loại pathname phù hợp với route. Ví dụ: <Route path='/roster'/> sẽ khớp với tên đường dẫn bắt đầu bằng / roster. Khi tên đường dẫn của vị trí hiện tại được khớp bởi Route, Route sẽ hiển thị một phần tử React. Khi đường dẫn không khớp, Route sẽ không hiển thị bất cứ điều gì.

<Route path='/roster'/>
// Khi pathname là "/", đường dẫn sẽ không khớp.
// Khi pathname là '/roster' or '/roster/2' thì đường dẫn sẽ khớp.
// Nếu bạn chỉ muốn khớp với '/roster', bạn cần sử dùng "exact". Đường dẫn sẽ khớp với "/roster" nhưng không khớp với "/roster/2"
// '/roster/2'.
<Route exact path='/roster'/>

Khi route's path khớp, một đối tượng match với các thuộc tính sau sẽ được tạo ra:

  • url — phần kết hợp của tên đường dẫn với vị trí hiện tại (current location's pathname)
  • path — đường dẫn route
  • isExact —path === pathname
  • params — một đối tượng có chứa các giá trị từ tên đường dẫn đã bị bắt.

Khởi tạo bộ định tuyến

<Route> có thể được tạo ra bất cứ nơi nào bên trong của router, nhưng thường nó sẽ được tổ chức ở cùng một nơi. Bạn có thể sử dụng thành phần <Switch> để nhóm <Route>. <Switch> sẽ lặp qua các phần tử con của nó (routes) và chỉ render đầu tiên phù hợp với tên đường dẫn hiện tại.
Để khớp một đường dẫn trong ứng dụng, tất cả những gì mình phải làm là tạo một phần tử <Route> với đường dẫn mà chúng ta muốn kết hợp.

<Switch>
  <Route exact path='/' component={Home}/>
  {/* both /roster and /roster/:number begin with /roster */}
  <Route path='/roster' component={Roster}/>
  <Route path='/schedule' component={Schedule}/>
</Switch>

Routes có ba đạo cụ có thể được sử dụng để xác định những gì nên được kết xuất khi paht của route phù hợp.Tuy nhiên, chỉ nên cung cấp một phần cho một phần tử <Route>.

  • Component - React Components, Khi một route với một thành phần phù hợp, route sẽ trả về một phần tử mới có kiểu là thành phần React được cung cấp (được tạo ra bằng cách sử dụng React.createElement).
  • render - Một hàm trả về một phần tử React. Nó sẽ được gọi khi đường dẫn phù hợp. Điều này tương tự như components, nhưng hữu ích cho việc inline rendering và chuyển các props bổ sung cho phần tử.
  • children  - Một hàm trả về một phần tử React. Không giống như hai đạo cụ trước, điều này sẽ luôn được render, bất kể đường dẫn của tuyến đường có phù hợp với vị trí hiện tại hay không.
<Route path='/page' component={Page} />
const extraProps = { color: 'red' }
<Route path='/page' render={(props) => (
  <Page {...props} data={extraProps}/>
)}/>
<Route path='/page' children={(props) => (
  props.match
    ? <Page {...props}/>
    : <EmptyPage {...props}/>
)}/>

Thông thường, phải sử dụng component hoặc render. children props đôi khi có thể hữu ích, nhưng thông thường nó sẽ thích hợp hơn khi để không có gì nếu đường dẫn không phù hợp. Mình không có bất kỳ đạo cụ bổ sung để đi qua các thành phần, do đó, mỗi <Route> của mình sẽ sử dụng component prop.
Bây giờ chúng ta đã tìm ra cấu trúc root route, chúng ta chỉ cần thực hiện các route của chúng ta. Đối với ứng dụng này, chúng ta sẽ tạo <Switch> và <Route> bên trong <Main> của chúng ta, sẽ đặt HTML được tạo ra bởi một route kết hợp bên trong nút <main> DOM.

import { Switch, Route } from 'react-router-dom'
const Main = () => (
  <main>
    <Switch>
      <Route exact path='/' component={Home}/>
      <Route path='/roster' component={Roster}/>
      <Route path='/schedule' component={Schedule}/>
    </Switch>
  </main>
)

Nested Routes

Làm thế nào để lồng các component lại với nhau? Router sẽ giúp ta làm điều đó khi lồng routes. Giờ, do ở trên chúng ta đã khai báo <Route path='/roster'> và giờ ta cần profile của từng roster ta có thể viết như sau:

const Roster = () => (
  <Switch>
    <Route path='/roster/:number' component={Player}>
        <Route path="profiles" component={PlayerProfile} />
    </Route>
  </Switch>
)

Path Params

Đôi khi có các biến trong một tên đường dẫn mà chúng ta muốn nắm bắt. Ví dụ: với route profile của người chơi, chúng tôi muốn nắm bắt số của player. Chúng ta có thể làm điều này bằng cách thêm params đường dẫn vào chuỗi đường dẫn của route của chúng ta.
Phần :number của đường dẫn /roster/:number nghĩa là phần sau của /roster/ sẽ bị bắt và lưu vào match.params.number Từ đấy,

component có thể sử dụng đối tượng props.match.params để xác định dữ liệu của player cần được hiển thị.

// an API that returns a player object
import PlayerAPI from './PlayerAPI'
const Player = (props) => {
  const player = PlayerAPI.get(
    parseInt(props.match.params.number, 10)
  )
  if (!player) {
    return <div>Sorry, but the player was not found</div>
  }
  return (
    <div>
      <h1>{player.name} (#{player.number})</h1>
      <h2>{player.position}</h2>
    </div>
)

bạn có thể đọc thêm về path params ở đây

Link

Cuối cùng, ứng dụng cần một cách để điều hướng giữa các trang. Nếu chúng ta tạo các liên kết bằng cách sử dụng các phần tử neo, nhấp vào chúng sẽ gây ra toàn bộ trang phải tải lại. React Router cung cấp một thành phần <Link> để ngăn chặn điều đó xảy ra. Khi nhấp vào <Link>, URL sẽ được cập nhật và nội dung được hiển thị sẽ thay đổi mà không cần tải lại trang.

import { Link } from 'react-router-dom'
const Header = () => (
  <header>
    <nav>
      <ul>
        <li><Link to='/'>Home</Link></li>
        <li><Link to='/roster'>Roster</Link></li>
        <li><Link to='/schedule'>Schedule</Link></li>
      </ul>
    </nav>
  </header>
)

Trên đây là những gì mình đã đọc và hiểu được về React router, con rất nhiều điều còn thiếu hoặc mình hiểu sai, mong các bạn góp ý để bài viết trở nên hữu ích hơn.

Dưới đây là một số link mà mình đã tham khảo https://medium.com/@pshrmn/# https://viblo.asia/p/react-router-JQVGVzDMGyd#ii2-nested-routes-3 https://www.youtube.com/watch?v=eofpZPRUnP8