簡單介紹常用的 react 處理用戶端路徑的 library - react router。
react 是一個 SPA,Single-page application 的前端框架,也就是畫面上所有的內容都是透過元件的切換做到切換不同分頁的效果,如果今天有某個產品的頁面希望可以分享給別人的時候,因為實際上網頁路徑並沒有改變,所以分享給其他人的時候都會是同一個頁面,也沒有辦法做到回到上一頁的行為。
這個時候可以透過 react router 來協助我們完成這些事情。
npm install react-router-dom
oryarn add react-router-dom
可以在任何地方創建或是定義你的路徑,不過通常會在 root 的地方,這邊就在 App.tsx
的檔案裡定義我們的路徑。
假設我希望我有個路徑像下面這樣,有一個首頁也有 products 跟 user 頁面。
root - home page
├── products
└── user
從 react-router-dom 引入兩個重要的東西,RouterProvider
元件跟 createBrowserRouter
。
createBrowserRouter
: 用來建立 router 路徑的 method,接收兩個參數第一個參數是個 array,裡面放著所有路徑的資料,第二個參數是選填參數,用來設定跟路徑相關內容。
RouterProvider
: react-router 所提供的元件,這個元件有兩個常用到的 props,router
跟 fallbackElement
。
router
裡面放著使用 createBrowserRouter
所建立出來的 router 物件。fallbackElement
跟我們上一篇介紹到的 Suspense 裡用到的 fallback 一樣,是當 router 還在載入的過程時用來顯示的備用元素。簡單介紹完了如何建立路徑,那就把上面的三個路徑建立出來吧。
import { createBrowserRouter, RouterProvider } from "react-router-dom";
const router = createBrowserRouter([
{
path: "/",
element: <div>Here is home page!</div>,
},
{
path: "/products",
element: <div>Here is products page!</div>,
},
{
path: "/user",
element: <div>Here is user page!</div>,
},
]);
function App() {
return <RouterProvider router={router} />;
}
這樣就完成三個路徑的建立了。
為了方便管理跟查看,這邊把 element 的部分替換成元件。
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import Home from "./pages/Home";
import Products from "./pages/Products";
import User from "./pages/User";
const router = createBrowserRouter([
{
path: "/",
element: <Home />,
},
{
path: "/products",
element: <Products />,
},
{
path: "/user",
element: <User />,
},
]);
function App() {
return <RouterProvider router={router} />;
}
這樣的確建立了三個獨立的分頁也可以透過輸入網址的方式來顯示頁面。
每一個 router 物件裡面還可以在放入一個 children 的屬性。
來代表這個之間的從屬關係,所以 products 跟 user 應該都要在 home 下面,應該長這個樣子。
const router = createBrowserRouter([
{
path: "/",
element: <Home />,
children: [
{
path: "/products",
element: <Products />,
},
{
path: "/user",
element: <User />,
},
],
},
]);
可以嘗試輸入網址來切換內容會發現雖然切換網址沒有問題但是會發現不管怎麼切換都只會有 home page 的資訊,products 跟 user 的內容都看不到了,因為當使用 children 的時候都路徑匹配正確的時候會把 children 裡面的路徑元件也傳到父層,但是我們並沒有在父層做使用所以當然無法看到內容。
要看到 children 的元件內容會需要透過 react-router-dom 的 Outlet
元件來把元件放到畫面上,就跟我們把內容透過 children 傳到子元件,但是子元件也需要把 children 放到 JSX 裡才看得到的意思是一樣的。
// Home.tsx
import { Link, Outlet } from "react-router-dom";
export default function Home() {
return (
<>
<ul>
<Link to="/">
<li>home</li>
</Link>
<Link to="/products">
<li>products</li>
</Link>
<Link to="/user">
<li>user</li>
</Link>
{/* 也可以寫相對路徑來回到 "上一層" */}
<Link to="..">
<li>上一層</li>
</Link>
</ul>
<div>Here is home page!</div>
{/* Outlet 會替換成子路徑的 react 元件 */}
<Outlet />
</>
);
}
另外在 react-router 裡面會透過 Link 元件來做路徑的切換,而不是使用 a 標籤,當然使用 a 標籤也可以,但是使用 a 標籤切換路徑的時候會發現每一次切換瀏覽器都會重整來進入畫面,等於是每次切換路徑都會重新發起一次新的 request 跟我們在網頁輸入網址沒有不同,透過 Link 標籤會幫我們判斷是否是 router 裡面既存的路徑,如果是的話會快速切換元件並改變網址,來達到切換路徑的效果。
當用戶輸入不存在的畫面時也希望可以給用戶一個錯誤畫面並把他引導去正確路徑。
這邊準備了一個 Error 的元件。
// pages/Error.tsx
import { Link } from "react-router-dom";
export default function Error() {
return (
<div>
<p>The path is no exist</p>
<p>back to home</p>
<Link to="/">Go Home</Link>
</div>
);
}
在 App.tsx 引入,並且在 router 物件裡加上 errorElement 的屬性跟 value。
const router = createBrowserRouter([
{
path: "/",
element: <Home />,
errorElement: <Error />, // 當路徑沒有匹配時顯示這一個元件
children: [
{
path: "/products",
element: <Products />,
},
{
path: "/user",
element: <User />,
},
],
},
]);
這樣當用戶輸入不存在的路徑的時候就可以引導他回到首頁。
另外也可以做出動態路徑,比如我們進到 user 畫面或是 product 頁面時,網址後面通常會帶著商品的編號作為動態路徑。
首先,新增我們的 Product 元件。
import { useParams } from "react-router-dom";
export default function Product() {
const { id } = useParams();
return (
<div>
Here is product page!
<p>Product ID is: {id} </p>
</div>
);
}
透過 react-router-dom 裡面提供的 useParams 可以取得動態路徑的裡面的內容。
然後在 createBrowserRouter
裡面新增新的 product 路徑。
const router = createBrowserRouter([
{
path: "/",
element: <Home />,
errorElement: <Error />,
children: [
{
path: "/products",
element: <Products />,
},
{
path: "/product/:id", // ":" 開頭代表動態路徑,id 則是參數的 key。
element: <Product />,
},
{
path: "/user",
element: <User />,
},
],
},
]);
接著稍微修改一下 Products.tsx
裡面的內容
// pages/Products.tsx
import { useNavigate } from "react-router-dom";
type Item = { id: string; name: string };
const products: Item[] = [
{ id: "001", name: "t-shirt" },
{ id: "002", name: "skirt" },
{ id: "003", name: "shoes" },
{ id: "004", name: "hat" },
{ id: "005", name: "jeans" },
{ id: "006", name: "dress" },
{ id: "007", name: "sweater" },
{ id: "008", name: "jacket" },
{ id: "009", name: "scarf" },
{ id: "010", name: "socks" },
];
export default function Products() {
const navigate = useNavigate();
function onClick(value: string) {
navigate(`/product/${value}`);
}
return (
<div>
Here is products page!
{products.map(({ name, id }) => (
<div key={id}>
<p>product name : {name}</p>
<button onClick={() => onClick(id)}>view Product page</button>
</div>
))}
</div>
);
}
除了使用 Link 元件來切換路徑以外,也可以透過 react-router-dom 提供的 useNavigate hook 來取得 navigate 這個 function,透過呼叫 function 放入路徑,也可以切換路徑。
我準備了一些假資料,用 map 把資料放到畫面上,點擊按鈕的時候會移動到 Product 頁面。
react router 還有很多其他的功能,這邊就待大家去慢慢發掘了。
下一篇簡單介紹 react 裡面 style 的工具 emotion。
如果內容有誤再麻煩大家指教,我會盡快修改。
這個系列的文章會同步更新在我個人的 Medium,歡迎大家來看看 👋👋👋
Medium