iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 20
1
Modern Web

React 30天系列 第 20

Day 20-使用react-router繼續擴增todos吧( ´▽` )ノ

  • 分享至 

  • xImage
  •  

前情提要:
昨天安裝了react router以及介紹了裡面登場的角色,今天就來套用在我們的萬年好朋友todos上吧!

一開始覺得很廢的todos,現在我要好好感謝你。
如果不是你,怎麼來驗證實作?
如果不是你,我去哪邊找現成專案給我玩?
感謝todos,我又可以對你毛手毛腳了。 (≧▽≦)

有鑒於之前看到了presentational和container components的區分,
看了Dan Abramov分享的這篇後,
抱著好奇和嘗試的心調整了一下,把layout和邏輯拆開,拆完後又自我懷疑了
在渲染TodoList這塊覺得拆開挺好的
但在新建todos的NewTodo覺得好像不是很需要,雖然我還是拆了它
是真的需要拆開呢?還是我只是為拆而拆?大概需要再多做幾個練習驗證XD
詳細內容可以參考github免得又佔版面惹

拆完之後就心無罣礙的開始安裝react router吧

yarn add react-router-dom

因為不想讓專案目錄下的index.js變得越來越複雜,所以另外新增了一個router.js檔案,建立RootRouter component,並且把有關router的設定都放在這邊。

  1. 從react-router-dom匯入BrowserRouter, Switch和Route,並將BrowserRouter重新命名為Router
  2. 使用Switch來匹配Route,這邊設定了三個Route,分別為首頁("/"),todos和news
  3. Route的prop需設定path以及component,這邊在首頁有另外加上exact。exact的作用是將path("/")做唯一匹配,否則不管是"/"、"/todos"或"/news"都會匹配到path為"/"的Route。
import React from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";

import Layout from "./src/components/Layout";
import Home from "./src/components/Home";
import Todos from "./src/components/Todos";
import News from "./src/components/News";

const RootRouter = () => {
  return (
    <Router>
      <Layout>
        <Switch>
          <Route exact path="/" component={Home} />
          <Route path="/todos" component={Todos} />
          <Route path="/news" component={News} />
        </Switch>
      </Layout>
    </Router>
  );
};

export default RootRouter;

看完上面的router.js可能會覺得有點不對勁
疑惑Layout component是哪裡跑來插花的角色,其實Layout只是React的Composition(組合)
router.js內<Layout>JSX中的任何内容都會被傳到Layout component中作為children props傳入,所以透過這樣組合的方式,我可以把Switch內容作為childrend傳入Layout使用,可以把它想像成畫框的感覺,Layout就是那個框,我只要匹配pathname就可以把內容component置換。

講完一個疑點後還有另一個**Fragments**,Fragments的功能就是幫我聚集多個react子元素,而且不會在DOM裡面增加節點的好幫手。
也就是說,我想要把Header和prop.children都渲染但不想用<div>把它們圈起來,因為<div>在這裡沒有任何功能,這時候就可以使用Fragment達到我的期望。
另外,<Fragment>其實還可以透過JSX的語法糖簡化成<>

import React, { Fragment } from "react";

import Header from "./Header";

const Layout = props => {
  return (
    <Fragment>
      <Header />
      {props.children}
    </Fragment>
  );
};

export default Layout;

接著,我打算在header放上導覽列Link到各個頁面,所以在Header component這邊,我從react-router-dom匯入了NavLink component,大家還記得他跟Link component差異在哪嗎?就是多了activeClassName可以用。
因為className設很長不想重複輸入,所以我把導覽列的項目資訊先存在navList裡,to是指匹配的path;text則是指導覽列的顯示文字。

import React from 'react';
import { NavLink } from "react-router-dom";

const navList = [
  { to: "/", text: "Home" },
  { to: "/todos", text: "Todos" },
  { to: "/news", text: "News" },
]

const Header = () => {
  return (
    <header className="header">
      <h1 className="header-heading">Heading</h1>
      <nav>
        <ul className="header-nav-list">
          { navList.map(({to, text}, index) => (
            <li key={index} className="header-nav-list-item">
              <NavLink
                exact
                className="list-item-anchor"
                activeClassName="list-item-anchor-active"
                to={to}>
                {text}
              </NavLink>
            </li>
          ))}
        </ul>
        </nav>
    </header>
  );
};

export default Header;

接著,我就可以開始建立那三頁(home, todos和news)要render的component,因為只有todos已經好了,所以其他頁面先很簡單的渲染標題吧!Home和News component目前都是一樣的狀態就不放上來佔空間了。

import React from 'react';

const Home = () => {
  return (
    <main className="main main-no-content">
      Home
    </main>
  );
};

export default Home;

全部完成後,回到專案目錄的index.js匯入RootRouter component,並把Provider內的component改成RootRouter就大功告成了~

import RootRouter from "./router";

ReactDOM.render(
  <Provider store={store}>
    <RootRouter />
  </Provider>,
  document.getElementById("app")
);

加上一些CSS的設定後實際操作如下:
https://i.imgur.com/D6BQYec.gif
可以看到透過NavLink產生的anchor可以切換不同path渲染不同的component。
因為NavLink有設定activeClassName的關係,所以我不需要判斷現在的pathname是什麼,就可以把該頁的nav link加上特定的className。

github傳送門


今日總結:
今天把react router的主要角色跟todos串接結合了,明天再來看看他們可以譜出什麼火花~


上一篇
Day 19-你好,React Router 4(安裝及角色介紹)
下一篇
Day 21-這裡不能來,閃閃去旁邊(Redirect)與No Match (404)
系列文
React 30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言