# React Router ## Component React Router的元件主要分三種: * routers,例如 `<BrowserRouter>` 和 `<HashRouter>` * route matchers: 例如`<Route>`和`<Switch>` * navigation:例如`<Link>`, `<NavLink>`, and `<Redirect>` ### routers `<BrowserRouter>` 和 `<HashRouter>` 主要的差別是 * BrowserRouter: http://example.com/your/page * HashRouter: http://example.com/#/your/page ### route matchers * Switch: 渲染`<Switch>` 時,它將搜索它的`<Route>`子元素,找到路徑與當前URL匹配並只會渲染第一個符合的`<Route>`元件。 * Route: `<Route>` 則是用來將符合匹配路徑的元件內容渲染出來。 ### navigation * `<Link>`: 用來導航至你要渲染出來的元件,在 HTML 中會是以 `<a>` 的方式呈現 * `<NavLink>`: 也是一種 `<Link>`,但差別在於可以自訂點擊該連結時的效果,透過搭配 activeClassName 來使用,並且當路徑中有 "/" 設定時,需搭配 exact 明確指出需要完整匹配路徑才可以觸發 active style * `<Redirect>`: 用於頁面重整等等時,作為重導向到指定路徑使用 [example](https://codesandbox.io/s/jolly-sanne-3g3lcm?file=/src/App.js) ## Hook React Router有提供四種Hook: * useHistory * useLocation * useParams * useRouteMatch ### useParams useParams回傳URL參數的值 沒有用Hook的話需要透過 this.props.match.params 的方式取得這個值。 [範例](https://codesandbox.io/s/react-router-url-parameters-forked-mfbyul?file=/example.js:0-1181) ### useRouteMatch useRouteMatch 可以確認是否URL是否匹配 沒有用Hook的話需要透過 `this.props.match.url` 或者 `this.props.match.path` 的方式取得 url 或者 path 才能進行匹配。 [範例](https://codesandbox.io/s/react-router-nesting-forked-zbr136?file=/example.js:712-718) ### useLocation useLocation會回傳 location 物件可以提供當前應用程式所在的位置資訊。 沒有用Hook的話需要透過 this.props.location 的方式才可以取得 location [範例](https://codesandbox.io/s/react-router-no-match-404-forked-yw0lvr?file=/example.js) ### useHistory useHistory 裡面提供一些方法操作導航的操作記錄,而其中一個常用的是 push,這個方法可以讓我們在觸發某些行為時,將網頁導航至我們設定的位置。 另外還有 `replace, go, goBack, goForward, block`等方法 [範例](https://codesandbox.io/s/funny-bogdan-2hg4m2?file=/example.js) ## 從 v5 到 v6 的差異 ### Switch 被替換為 Routes v5 ``` import { BrowserRouter, Switch } from "react-router-dom"; function App() { return ( <BrowserRouter> <div className="App"> <Switch> {" "} {/* 路由Route在此定義 */} </Switch> </div> </BrowserRouter> ); } export default App ``` v6 ``` import { BrowserRouter, Routes } from "react-router-dom"; function App() { return ( <BrowserRouter> <div className="App"> <Routes> {" "} {/* Switch 會被改成 Routes */} {/* 路由Route在此定義 */} </Routes> </div> </BrowserRouter> ); } export default App ``` ### Route元件更新 v5 ``` <Switch> <Route path="/pageA" component={Product} /> <Route path="/pageB"> <Product id={2} /> </Route> <Route path="/pageC" render={(props) => <Product {...props} />} /> </Switch> ``` v6 ``` <Routes> <Route path="/pageA" element={<Product />} /> {/* props */} <Route path="/pageB" element={<Product id={200} category="shirt" />} /> </Routes> ``` 另外 v6的Router 會抓取最相近的 url 去呈現對應的元件。 ``` <Routes> <Route path="/pageA" element={<Product />} /> <Route path="/pageB" element={<Topics />} /> <Route path="/pageB/:id" element={<SUbTopics />} /> <Route path="/" element={<Home />} /> </Routes> ``` ### NavLinks 刪除activeClassName和activeStyle v5 ``` import {NavLink} from “react-router-dom” {/* … */} <NavLink to="/product" style={{ color: "#689" }} activeStyle={{ color: "#3072c9" }} className="nav_link" activeClassName="active" > Products </NavLink> ``` v6 ``` <NavLink to="/product" style={({ isActive }) => ({ color: isActive ? "#3072c9" : "#689" })} className={({ isActive }) => `link${isActive ? " active" : ""}`} > Product </NavLink> <Navigate replace to="/about" /> ``` ### Nesting Route v5 ``` import { useRouteMatch } from "react-router-dom"; function App() { return ( <BrowserRouter> <Switch> <Route exact path="/about" component={About} /> <Route path="/product" component={Product} /> </Switch> </BrowserRouter> ); } function Product() { let match = useRouteMatch(); return ( <div> <Switch> <Route path={`${match.path}`}> <AllProducts /> </Route> <Route path={`${match.path}/:id`}> <ProductDetail /> </Route> </Switch> </div> ); } ``` v6 ``` import { Outlet } from "react-router-dom"; function App() { return ( <Routes> <Route path="/product" element={<Product />}> <Route path="/" element={<AllProducts />} /> <Route path="/:id" element={<ProductDetail />} /> </Route> </Routes> ); } function Product() { return ( <Container> <> <div>Product</div> {/* 父元件的其他內容 */} </> {/* 這是巢狀資訊開始的地方 */} <Outlet /> </Container> ); } ``` ### Navigate替代Redirect v5 ``` import { useHistory } from "react-router-dom"; function Product() { const history = useHistory(); const handleClick = () => { //這會將新路線推送到導航堆疊的頂部 history.push("/new-route"); //這會將當前路線替換為導航堆疊中的新路由 history.replace("/new-route"); }; return ( <div> <button>點選我重定向到新路由</button> </div> ); } ``` v6 ``` import { useNavigate } from "react-router-dom"; function Product() { const navigate = useNavigate(); const handleClick = () => { //這會將新路線推送到導航堆疊的頂部 navigate("/new-route"); //這會將當前路線替換為導航堆疊中的新路由 navigate("/new-route", { replace: true }); }; return ( <div> <button>點選我重定向到新路由</button> </div> ); } // Goes forward navigate(1) // Goes forward twice navigate(2) // Goes backward navigate(-1) // Goes backward three times navigate(-3) ``` ### 參考資料 https://reactrouter.com/docs/en/v6/getting-started/overview https://v5.reactrouter.com/web/example/basic https://ithelp.ithome.com.tw/articles/10282773