前言

好一段時間沒有發表學習文了,主要是因為我花了兩個禮拜的時間研究這個東西,終於找到了原因和使用方式。雖然有ChatGPT的幫助,但還是沒有解決我現在要特別標記的主題。

(P.S. 這次的主題是我卡了很久的一個技術問題。)

React Router 中 Outlet

在父路由元素中,應該使用 來呈現其子路由元素。這樣,當子路由被渲染時,嵌套的使用者介面就能夠顯示出來。如果父路由完全匹配,它會渲染一個子索引路由,如果沒有索引路由,則不會呈現任何內容。

1. 類型聲明 (Type declaration)

1
2
3
4
5
6
interface OutletProps {
context?: unknown;
}
declare function Outlet(
props: OutletProps
): React.ReactElement | null;

2. 參考官方作法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function Dashboard() {
return (
<div>
<h1>Dashboard</h1>

{/* This element will render either <DashboardMessages> when the URL is
"/messages", <DashboardTasks> at "/tasks", or null if it is "/"
*/}
<Outlet />
</div>
);
}

function App() {
return (
<Routes>
<Route path="/" element={<Dashboard />}>
<Route
path="messages"
element={<DashboardMessages />}
/>
<Route path="tasks" element={<DashboardTasks />} />
</Route>
</Routes>
);
}

前置作業

首先,將【index.tsx】放入要執行的Router中,這次的修改將以此為主要依據。

AppRouter 原始作法

讓我簡單解釋一下下面的程式碼的作用:

  1. Adminlte 需要加入 Js或是 css 才能使用他們的css

    1
    2
    3
    4
    5
    6
    //append css or js
    import 'admin-lte/dist/css/adminlte.min.css'
    import 'admin-lte/plugins/fontawesome-free/css/all.css'
    import 'admin-lte/plugins/bootstrap/js/bootstrap'
    import 'admin-lte/plugins/bootstrap/js/bootstrap.bundle'
    import 'admin-lte/dist/js/adminlte'
  2. 拆開的Layout
    其實,這部分應該解釋為元件,我將 Header 和 Menu 拆分出來,並將它們作為我的元件使用。實際上,我還是很習慣將它們歸類在MVC模式中的 Shared 資料夾中,所以我將其歸類在 Layout 中使用。

1
2
3
//append layout
import Header from '../component/@Shared/@Layout/Header';
import MenuSidebar from '../component/@Shared/@Layout/MenuSidebar';
  1. Router 路徑
    你可以看一下下面的程式碼。在使用 Layout 的地方,會有一些棘手的情況不適用於相同的版面,例如登入、註冊、首頁等。它們不符合 Layout 的定義。這就需要對程式碼進行一些修改。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    import React, { lazy } from 'react';
    import { BrowserRouter, Routes, Route, Link, Await } from 'react-router-dom';

    //append css or js
    import 'admin-lte/dist/css/adminlte.min.css'
    import 'admin-lte/plugins/fontawesome-free/css/all.css'
    import 'admin-lte/plugins/bootstrap/js/bootstrap'
    import 'admin-lte/plugins/bootstrap/js/bootstrap.bundle'
    import 'admin-lte/dist/js/adminlte'

    //append layout
    import Header from '../component/@Shared/@Layout/Header';
    import MenuSidebar from '../component/@Shared/@Layout/MenuSidebar';
    // append page (only views)
    import Home from '../component/@Views/TopList/Home';
    import About from '../component/@Views/TopList/About';

    import BDP000A from '../component/@Views/Systems/BDP000A/Index';
    import BDP000A_EditorForm from '../component/@Views/Systems/BDP000A/EditorForm';

    import ProfileHome from '../component/@Views/Profile/ProfileHome';
    import Alert from '../component/@Views/Template/Alert';
    import SampleTable from '../component/@Views/Template/SampleTable';
    import SampleReactTable from '../component/@Views/Template/SampleReactTable';

    import SystemConfig from '../component/@Views/Systems/SystemConfig/Index'

    const AppRouter = () => {
    return (
    <BrowserRouter>
    <div className="wrapper">
    <Header />
    <MenuSidebar />

    <div className='content-wrapper'>
    <Routes>
    <Route path="/" element={<Home />} />
    <Route path="/about" element={<About />} />
    </Routes>

    <Routes>
    <Route path="/BDP000A" element={<SystemConfig />} />
    <Route path="/BDP000A/:key" element={<BDP000A_EditorForm />} />


    <Route path="/Profile" element={<ProfileHome />} />
    <Route path="/Menu" element={<SampleReactTable />} />
    <Route path="/Temp/Alert" element={<Alert />} />
    <Route path="/Temp/Table" element={<SampleTable />} />
    </Routes>

    </div>
    </div>
    </BrowserRouter>
    );
    }

    export default AppRouter;

變更方式

1. 將Layout拆開

  1. 創建 MainLayout.tsx

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    import { ReactNode } from "react";
    import Header from "./Header";
    import MenuSidebar from "./MenuSidebar";
    //append css or js
    import 'admin-lte/dist/css/adminlte.min.css'
    import 'admin-lte/plugins/fontawesome-free/css/all.css'
    import 'admin-lte/plugins/bootstrap/js/bootstrap'
    import 'admin-lte/plugins/bootstrap/js/bootstrap.bundle'
    import 'admin-lte/dist/js/adminlte'
    import { Outlet } from "react-router-dom";

    const MainLayout = () => {
    return (
    <div className="wrapper">
    <Header />
    <MenuSidebar />

    <div className='content-wrapper'>
    <Outlet />
    </div>
    </div>
    );
    };

    export default MainLayout;
  2. 創建LoginLayout.tsx

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import { ReactNode } from "react";
    import Login from "../../@Views/Login/Index";
    import { Outlet } from "react-router-dom";

    const LoginLayout = () => {
    return (
    <div>
    <Outlet />
    </div>
    );
    };

    export default LoginLayout;

2. 將 AppRouter 加入Layout版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import React, { lazy } from 'react';
import { BrowserRouter, Routes, Route, Link, Await } from 'react-router-dom';

// append page (only views)
import Home from '../component/@Views/TopList/Home';
import About from '../component/@Views/TopList/About';

import BDP000A from '../component/@Views/Systems/BDP000A/Index';
import BDP000A_EditorForm from '../component/@Views/Systems/BDP000A/EditorForm';

import ProfileHome from '../component/@Views/Profile/ProfileHome';
import Alert from '../component/@Views/Template/Alert';
import SampleTable from '../component/@Views/Template/SampleTable';
import SampleReactTable from '../component/@Views/Template/SampleReactTable';

import SystemConfig from '../component/@Views/Systems/SystemConfig/Index'
import LoginLayout from '../component/@Shared/@Layout/LoginLayout';
import Login from '../component/@Views/Login/Index';
import MainLayout from '../component/@Shared/@Layout/MainLayout';

const AppRouter = () => {
return (
<BrowserRouter>
<Routes>
<Route path="/Login" element={<LoginLayout />}>
<Route path="/Login" element={<Login />} />
</Route>

<Route path="/" element={<MainLayout />}>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Route>
</Routes>
</BrowserRouter>
);
}

export default AppRouter;

參考文件

  1. [Bonus 系列] - 來看看 React Router v6 有什麼新功能?和 v5 有哪些地方不同?
  2. React Router