2021 react-router v6 快速入門

使用官方的教學項目

npx create-react-app router-tutorial

安裝 react-router 依賴

cd router-tutorial
npm add react-router-dom@6 history@5

react-router-dom是瀏覽器端的基于react-router庫的庫涡匀,所以裝了這個以后就不用再手動裝react-router了

修改App.js和 index.js到簡單的樣子

//src/App.js
export default function App() {
  return (
    <div>
      <h1>Bookkeeper!</h1>
    </div>
  );
}
// src/index.js
import { render } from "react-dom";
import App from "./App";

const rootElement = document.getElementById("root");
render(<App />, rootElement);

然后啟動項目,然后我們可以在基礎上修改了辅鲸。

# probably this
npm start

# or this
npm run dev

#or
yarn start

01.BrowserRouter

連接你的app到瀏覽器的URL确徙。

用BrowserRouter包裹在你的App的外面

//src/index.js
import { render } from "react-dom";
import { BrowserRouter } from "react-router-dom";
import App from "./App";

const rootElement = document.getElementById("root");
render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  rootElement
);

02.添加一些鏈接Link

src/App.js里添加一些鏈接和全局導航饱亮。

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

export default function App() {
  return (
    <div>
      <h1>Bookkeeper</h1>
      <nav
        style={{
          borderBottom: "solid 1px",
          paddingBottom: "1rem"
        }}
      >
        <Link to="/invoices">Invoices</Link> |{" "}
        <Link to="/expenses">Expenses</Link>
      </nav>
    </div>
  );
}

現(xiàn)在點擊那些鏈接,你會發(fā)現(xiàn)地址欄會發(fā)生改變,也可以用前進后退在歷史記錄中移動

03.添加一些路由

  • src/routes/invoices.jsx
  • src/routes/expenses.jsx
//src/routes/expenses.jsx
export default function Expenses() {
  return (
    <main style={{ padding: "1rem 0" }}>
      <h2>Expenses</h2>
    </main>
  );
}
//src/routes/invoices.jsx
export default function Invoices() {
  return (
    <main style={{ padding: "1rem 0" }}>
      <h2>Invoices</h2>
    </main>
  );
}

接下來我們需要在index.js里面創(chuàng)建路由配置告訴app如何渲染不同的url

//src/index.js
import { render } from "react-dom";
import {
  BrowserRouter,
  Routes,
  Route
} from "react-router-dom";
import App from "./App";
import Expenses from "./routes/expenses";
import Invoices from "./routes/invoices";

const rootElement = document.getElementById("root");
render(
  <BrowserRouter>
    <Routes>
      <Route path="/" element={<App />} />
      <Route path="expenses" element={<Expenses />} />
      <Route path="invoices" element={<Invoices />} />
    </Routes>
  </BrowserRouter>,
  rootElement
);

04.嵌套路由

我們注意到點擊鏈接的時候,App中的布局消失了衣迷。只剩下Expenses或Invoices這兩個路由指向的內(nèi)容

嵌套路由的作用就是共享部分UI

我們需要兩步操作實現(xiàn)這一點

首先index.js里面對路由進行嵌套。這樣兩個組件就變成了App組件的子節(jié)點

import { render } from "react-dom";
import {
  BrowserRouter,
  Routes,
  Route
} from "react-router-dom";
import App from "./App";
import Expenses from "./routes/expenses";
import Invoices from "./routes/invoices";

const rootElement = document.getElementById("root");
render(
  <BrowserRouter>
    <Routes>
      <Route path="/" element={<App />}>
        <Route path="expenses" element={<Expenses />} />
        <Route path="invoices" element={<Invoices />} />
      </Route>
    </Routes>
  </BrowserRouter>,
  rootElement
);

當路由擁有子節(jié)點的時候會發(fā)生兩件事

  1. 路由的url嵌套 ("/" + "expenses" and "/" + "invoices")
  2. 子路由組件匹配的時候也會渲染父組件共享的部分

接下來我們在App.jsx添加一個Outlet作為渲染子節(jié)點路由的地方

//src/App.jsx
import { Outlet, Link } from "react-router-dom";

export default function App() {
  return (
    <div>
      <h1>Bookkeeper</h1>
      <nav
        style={{
          borderBottom: "solid 1px",
          paddingBottom: "1rem"
        }}
      >
        <Link to="/invoices">Invoices</Link> |{" "}
        <Link to="/expenses">Expenses</Link>
      </nav>
      <Outlet />
    </div>
  );
}

這下我們就可以在兩個路由間切換保持共享的布局了酱酬。

05.給Invoices路由添加數(shù)據(jù)

我們模擬真實使用場景壶谒,給Invoices路由造點假數(shù)據(jù)

// src/data.js
let invoices = [
  {
    name: "Santa Monica",
    number: 1995,
    amount: "$10,800",
    due: "12/05/1995"
  },
  {
    name: "Stankonia",
    number: 2000,
    amount: "$8,000",
    due: "10/31/2000"
  },
  {
    name: "Ocean Avenue",
    number: 2003,
    amount: "$9,500",
    due: "07/22/2003"
  },
  {
    name: "Tubthumper",
    number: 1997,
    amount: "$14,000",
    due: "09/01/1997"
  },
  {
    name: "Wide Open Spaces",
    number: 1998,
    amount: "$4,600",
    due: "01/27/2998"
  }
];

export function getInvoices() {
  return invoices;
}

然后我們修改invoices.jsx組件,獲取并且渲染數(shù)據(jù)

//src/routes/invoices.jsx
import { Link } from "react-router-dom";
import { getInvoices } from "../data";

export default function Invoices() {
  let invoices = getInvoices();
  return (
    <div style={{ display: "flex" }}>
      <nav
        style={{
          borderRight: "solid 1px",
          padding: "1rem"
        }}
      >
        {invoices.map(invoice => (
          <Link
            style={{ display: "block", margin: "1rem 0" }}
            to={`/invoices/${invoice.number}`}
            key={invoice.number}
          >
            {invoice.name}
          </Link>
        ))}
      </nav>
    </div>
  );
}

06.添加一個不匹配路由

我們可以發(fā)現(xiàn)膳沽,當我們輸入一個沒有分配地址的路由的時候汗菜,會顯示空白頁。

實際上有一個好辦法就是把這些不匹配的路由都導入一個404頁面挑社。

我們添加一個"*"路由陨界,這個路由會匹配所有沒有匹配其他路由的路由

// src/index.js
<Routes>
  <Route path="/" element={<App />}>
    <Route path="expenses" element={<Expenses />} />
    <Route path="invoices" element={<Invoices />} />
    <Route
      path="*"
      element={
        <main style={{ padding: "1rem" }}>
          <p>There's nothing here!</p>
        </main>
      }
    />
  </Route>
</Routes>

07.讀取url參數(shù)

下面我們添加一些新組件,用于顯示固定年份的invoice

// src/routes/invoice.jsx
export default function Invoice() {
  return <h2>Invoice #???</h2>;
}

然后我們在invoices路由下面添加這個子路由

// src/index.js
<Routes>
  <Route path="/" element={<App />}>
    <Route path="expenses" element={<Expenses />} />
    <Route path="invoices" element={<Invoices />}>
      <Route path=":invoiceId" element={<Invoice />} />
    </Route>
    <Route
      path="*"
      element={
        <main style={{ padding: "1rem" }}>
          <p>There's nothing here!</p>
        </main>
      }
    />
  </Route>
</Routes>

我們剛剛創(chuàng)建的路由是匹配 "/invoices/2005" and "/invoices/1998"這種格式的滔灶。

然后我們還需要在invoices.jsx 添加一個outlet普碎,不然顯示不出來子路由的內(nèi)容

然后我們在invoice.jsx 文件中獲取url參數(shù)

// src/routes/invoice.jsx
import { useParams } from "react-router-dom";

export default function Invoice() {
  let params = useParams();
  return <h2>Invoice: {params.invoiceId}</h2>;
}

接著我們在data.js里面添加一個根據(jù)年份返回對應年份數(shù)據(jù)的函數(shù)

//...
export function getInvoices() {
  return invoices;
}

export function getInvoice(number) {
  return invoices.find(
    invoice => invoice.number === number
  );
}

然后我們就能用這個函數(shù)獲取數(shù)據(jù)并且渲染出來了。

import { useParams } from "react-router-dom";
import { getInvoice } from "../data";

export default function Invoice() {
  let params = useParams();
  let invoice = getInvoice(parseInt(params.invoiceId, 10));
  return (
    <main style={{ padding: "1rem" }}>
      <h2>Total Due: {invoice.amount}</h2>
      <p>
        {invoice.name}: {invoice.number}
      </p>
      <p>Due Date: {invoice.due}</p>
    </main>
  );
}

08.index路由

這可能是react router 里面最難理解的概念录平。

當我們?yōu)g覽 invoices 路由的子路由內(nèi)容麻车,之后我們點擊invoices路由的鏈接,我們發(fā)現(xiàn)右側(cè)變成了空白斗这。

我們可以添加一個index路由解決這個問題

// src/index.js

<Routes>
  <Route path="/" element={<App />}>
    <Route path="expenses" element={<Expenses />} />
    <Route path="invoices" element={<Invoices />}>
      <Route
        index
        element={
          <main style={{ padding: "1rem" }}>
            <p>Select an invoice</p>
          </main>
        }
      />
      <Route path=":invoiceId" element={<Invoice />} />
    </Route>
    <Route
      path="*"
      element={
        <main style={{ padding: "1rem" }}>
          <p>There's nothing here!</p>
        </main>
      }
    />
  </Route>
</Routes>

接下來我們發(fā)現(xiàn)點擊invoices路由的時候會默認顯示index路由的內(nèi)容而不是空白动猬。

index路由和其他路由不同的地方是它沒有path屬性,他和父路由共享同一個路徑表箭。

下面幾點可以幫助你理解這個概念

  • index路由渲染在父路由的outlet赁咙,而且路由地址和父路由相同
  • index路由在父路由匹配并且其他子路由不匹配的時候 匹配
  • index路由是一個父節(jié)點默認的子節(jié)點
  • index路由在用戶還沒有點擊導航中的鏈接時渲染

09.高亮激活的鏈接

通常我們需要,特別是在導航列表里面免钻,需要展示給用戶當前激活的鏈接是哪個彼水。

西面我們把invoices.jsx中的Link換成NavLink

import { NavLink, Outlet } from "react-router-dom";
import { getInvoices } from "../data";

export default function Invoices() {
  let invoices = getInvoices();
  return (
    <div style={{ display: "flex" }}>
      <nav
        style={{
          borderRight: "solid 1px",
          padding: "1rem"
        }}
      >
        {invoices.map(invoice => (
          <NavLink
            style={({ isActive }) => {
              return {
                display: "block",
                margin: "1rem 0",
                color: isActive ? "red" : ""
              };
            }}
            to={`/invoices/${invoice.number}`}
            key={invoice.number}
          >
            {invoice.name}
          </NavLink>
        ))}
      </nav>
      <Outlet />
    </div>
  );
}

我們做了以下3件事

  1. 替換Link為NavLink

  2. 我們用函數(shù)改變樣式

  3. 我們該百年了鏈接顏色通過NavLink傳遞過來的isActive屬性

我們也可以利用className做到一樣的效果。

// normal string
<NavLink className="red" />

// function
<NavLink className={({ isActive }) => isActive ? "red" : "blue"} />

10.url搜索參數(shù)

搜索參數(shù)就類似于url參數(shù)极舔,但是他們在url中所處的位置不同凤覆。

不是由/分隔,他們出現(xiàn)在一個?之后拆魏,類似于這樣的形式 "/login?success=1"or"/shoes?brand=nike&sort=asc&sortby=price

react router 提供了 useSearchParams 用于讀取和操作搜索參數(shù)盯桦。它有點像useState,不同點是useState是操作內(nèi)存中的數(shù)據(jù)渤刃,

而他是設置url 搜索參數(shù)中的state

// routes/invoices.jsx
import {
  NavLink,
  Outlet,
  useSearchParams
} from "react-router-dom";
import { getInvoices } from "../data";

export default function Invoices() {
  let invoices = getInvoices();
  let [searchParams, setSearchParams] = useSearchParams();

  return (
    <div style={{ display: "flex" }}>
      <nav
        style={{
          borderRight: "solid 1px",
          padding: "1rem"
        }}
      >
        <input
          value={searchParams.get("filter") || ""}
          onChange={event => {
            let filter = event.target.value;
            if (filter) {
              setSearchParams({ filter });
            } else {
              setSearchParams({});
            }
          }}
        />
        {invoices
          .filter(invoice => {
            let filter = searchParams.get("filter");
            if (!filter) return true;
            let name = invoice.name.toLowerCase();
            return name.startsWith(filter.toLowerCase());
          })
          .map(invoice => (
            <NavLink
              style={({ isActive }) => ({
                display: "block",
                margin: "1rem 0",
                color: isActive ? "red" : ""
              })}
              to={`/invoices/${invoice.number}`}
              key={invoice.number}
            >
              {invoice.name}
            </NavLink>
          ))}
      </nav>
      <Outlet />
    </div>
  );
}

11.自定義行為

接著上一節(jié)的程序拥峦,

我們發(fā)現(xiàn)如果我們點擊了過濾出來的鏈接,就不會保持過濾了卖子,input會被清空略号。

我們能夠在點擊新鏈接的時候保持查詢字符串,只要我們組合 Navlink和useLocation,組成一個我們自己的QueryNavLink

import { useLocation, NavLink } from "react-router-dom";

function QueryNavLink({ to, ...props }) {
  let location = useLocation();
  return <NavLink to={to + location.search} {...props} />;
}

類似于 useSearchParams, useLocation 也會返回一個location告訴我們一些信息。就類似于下面的格式

{
  pathame: "/invoices",
  search: "?filter=sa",
  hash: "",
  state: null,
  key: "ae4cz2j"
}

12.編程式導航

有時我們需要更改URL玄柠,

我們添加一個按鈕氛琢,將invoice刪除,然后導航到索引路由随闪。

首先我們在data.js里添加下面用于刪除一個invoice的函數(shù)

export function deleteInvoice(number) {
  invoices = invoices.filter(
    invoice => invoice.number !== number
  );
}

然后我們添加刪除按鈕

// src/routes/invoice.jsx
import { useParams, useNavigate } from "react-router-dom";
import { getInvoice, deleteInvoice } from "../data";

export default function Invoice() {
  let navigate = useNavigate();
  let params = useParams();
  let invoice = getInvoice(parseInt(params.invoiceId, 10));

  return (
    <main style={{ padding: "1rem" }}>
      <h2>Total Due: {invoice.amount}</h2>
      <p>
        {invoice.name}: {invoice.number}
      </p>
      <p>Due Date: {invoice.due}</p>
      <p>
        <button
          onClick={() => {
            deleteInvoice(invoice.number);
            navigate("/invoices");
          }}
        >
          Delete
        </button>
      </p>
    </main>
  );
}
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市骚勘,隨后出現(xiàn)的幾起案子铐伴,更是在濱河造成了極大的恐慌,老刑警劉巖俏讹,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件当宴,死亡現(xiàn)場離奇詭異,居然都是意外死亡泽疆,警方通過查閱死者的電腦和手機户矢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來殉疼,“玉大人梯浪,你說我怎么就攤上這事∑澳龋” “怎么了挂洛?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長眠砾。 經(jīng)常有香客問我虏劲,道長,這世上最難降的妖魔是什么褒颈? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任柒巫,我火速辦了婚禮,結(jié)果婚禮上谷丸,老公的妹妹穿的比我還像新娘堡掏。我一直安慰自己,他們只是感情好淤井,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布布疼。 她就那樣靜靜地躺著,像睡著了一般币狠。 火紅的嫁衣襯著肌膚如雪游两。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天漩绵,我揣著相機與錄音贱案,去河邊找鬼。 笑死,一個胖子當著我的面吹牛宝踪,可吹牛的內(nèi)容都是我干的侨糟。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼瘩燥,長吁一口氣:“原來是場噩夢啊……” “哼秕重!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起厉膀,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤溶耘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后服鹅,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凳兵,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年企软,在試婚紗的時候發(fā)現(xiàn)自己被綠了庐扫。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡仗哨,死狀恐怖形庭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情藻治,我是刑警寧澤碘勉,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站桩卵,受9級特大地震影響验靡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜雏节,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一胜嗓、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧钩乍,春花似錦辞州、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至涝涤,卻和暖如春媚狰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背阔拳。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工崭孤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓辨宠,卻偏偏與公主長得像遗锣,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子嗤形,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內(nèi)容

  • react-router 本來想給大家教學react-router2.0的版本精偿。但是考慮到4.0的版本已經(jīng)出現(xiàn)了。...
    Kris_lee閱讀 5,593評論 0 4
  • 導語: 其實demo早在四五月份就已經(jīng)寫好了赋兵,只是那段時間后臺項目太忙还最,加上自己拖延癥晚期,到最近才寫這篇文章毡惜。文...
    wuqke閱讀 3,996評論 0 6
  • React Router更新到4.0,使用起來發(fā)生了很大的變化斯撮,這個變化只要是指完全的組件化-----嵌套的JSX...
    Veb閱讀 1,971評論 5 18
  • react+redux+router入門總結(jié) 目錄 構(gòu)建配置 React組件经伙、css module React R...
    chenxingyu_o閱讀 926評論 0 0
  • React-Router v4 1. 設計理念1.1. 動態(tài)路由1.2. 嵌套路由1.3. 響應式路由 2. 快速...
    wlszouc閱讀 8,558評論 0 14