React Router

Khi nghe đến một Single Page App (SPA) thông thường mọi người sẽ hình dung đến việc mọi thứ chỉ chạy trên 1 view và không cần đến việc sử dụng Route. Tuy nhiên điều này là hoàn toàn sai lầm và sẽ gây ra những rắc rối khá lớn đối với những ứng dụng lớn (Facebook, Instagram, hay chính Viblo).

Việc phân chia ra các Route một cách rõ ràng sẽ có hiệu quả khá tốt đối với cả việc phát triển ứng dụng cũng như đem đến sự dễ dàng sử dụng cho người dùng.

Chính vì vậy, hôm nay mình sẽ giới thiệu với mọi người một module giúp cho việc điều hướng trong React trở nên dễ dàng hơn: react-router. mà cụ thể là v4 của nó nhé !

1, Cài đặt:

Hãy thử tạo một ví dụ nho nhỏ (lưu ý là mình sẽ viết không đúng chuẩn đâu nhé, vì là ví dụ nhỏ thôi).

Đầu tiên, hãy tạo một app react demo bằng create-react-app:

npm install -g create-react-app
create-react-app demo-app
cd demo-app

Mở terminal trong ứng dụng react của bạn, để có thể sử dụng react-router, bạn cần cài đặt nó thông qua npm trước:

npm install react-router-dom

Sau đó chúng ta sẽ sửa file App.js

import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";

function Index() {
  return <h2>Home</h2>;
}

function About() {
  return <h2>About</h2>;
}

function Users() {
  return <h2>Users</h2>;
}

function App() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/about/">About</Link>
            </li>
            <li>
              <Link to="/users/">Users</Link>
            </li>
          </ul>
        </nav>

        <Route path="/" exact component={Index} />
        <Route path="/about/" component={About} />
        <Route path="/users/" component={Users} />
      </div>
    </Router>
  );
}

export default App;

Khi đó ứng dụng của chúng ta sẽ có giao diện như sau:

Khi chuyển sang About:

Có thể thấy được rằng react-router đã hoạt động ^_^

2, Giải thích:

Qua ví dụ trên thì các bạn có thể thấy một vài thành phần quan trọng trong react-router:

Router:

Một <Router> sẽ sử dụng các HTML5 history Api (pushState, replaceState và sự kiện popstate) để khiến cho UI ứng dụng của bạn được đồng bộ với Url

Route:

<Route> là component quan trọng nhất trong react-router. Như các bạn có thể thấy, một <Route> nhận các props là "to""component".

Trong đó, "to" là uri ta cần sử dụng <Route> đó, còn "component" là component sẽ được render ra khi ta request đến uri tương ứng với uri được cung cấp ở "to".

Chú ý: Trong <Route> còn có exact dùng để chỉ định <Route> mặc định khi mới truy cập ứng dụng (tương ứng với to="/" )

Link:

Thẻ này thì không quá đặc biệt, nó sẽ được render ra tag <a> trong HTML với href ứng với to của thẻ <Link>

3, Một vài lỗi thường gặp:

Để nhiều hơn 1 child trong tag <Router>

Điều này cũng tương tự như việc return trong React chỉ được trả về duy nhất 1 child. Như trong ví dụ trên, nếu như ta bỏ thẻ <div> bao ngoài đi, chỉ còn các thẻ <nav><Route> ở bên trong, ta sẽ gặp phải lỗi:

error: React.Children.only expected to receive a single React element child

Vậy nên hãy luôn bao các thẻ khác bên trong một thẻ <div> trước khi để vào trong thẻ <Router> nhé !

Sử dụng thẻ <Link> ở một component bên ngoài rồi import vào file App.js thì app bị crash:

Hồi mới sử dụng react-router, mình đã phải tìm cách khắc phục lỗi dở hơi này, đại khái nó như sau:

//App.js
import React from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
import Header from "./Header";

function App() {
  return (
    <Router>
      <div>
        <Header />

        <Route exact path="/" component={Home} />
        <Route path="/about" component={About} />
        <Route path="/topics" component={Topics} />
      </div>
    </Router>
  );
}

function Home() {
  return <h2>Home</h2>;
}

function About() {
  return <h2>About</h2>;
}
    
export default About;
import React from "react";
import { BrowserRouter as Link } from "react-router-dom";
    
function Header() {
  return (
    <ul>
      <li>
        <Link to="/">Home</Link>
      </li>
      <li>
        <Link to="/about">About</Link>
      </li>
      <li>
        <Link to="/topics">Topics</Link>
      </li>
    </ul>
  );
}
    
export default Header;

Số là khi mình viết như trên, ứng dụng liên tục báo lỗi:

error: React.Children.only expected to receive a single React element child

Ơ, rõ là mình có bao toàn bộ các thẻ con ở trong 1 thẻ <div> rồi mới đưa vào thẻ <Router> cơ mà, tại sao lại xuất hiện lỗi này nhỉ ?

Sau một lúc google, mình đã phát hiện ra do dòng sau:

import { BrowserRouter as Link } from "react-router-dom";

Đây chính là lý do của việc copy không nhìn, BrowserRouter chỉ được import as Router, chứ không hề có Link, vậy nên mới xảy ra lỗi kỳ cục này. Chắc bạn thấy nó khá dở hơi, tuy nhiên thì khi mình google, thấy rất rất nhiều người cũng mắc phải lỗi như mình.

4, Tài liệu tham khảo:

https://reacttraining.com/react-router/

https://www.npmjs.com/package/react-router

https://developer.mozilla.org/en-US/docs/Web/API/History