iT邦幫忙

2023 iThome 鐵人賽

DAY 3
0

主架構建立

每個React網頁 都是由基礎價夠堆疊出來,對於使用者,只能看到結果
https://ithelp.ithome.com.tw/upload/images/20230907/20158340iyui74QJZG.jpg

所以按照元件一層層嵌套下去,當然沒問題。但是如果最底層的按鈕要返回 他已經登出這件事情怎麼辦呢?

npm 安裝

npm i react-router-dom

一樣是從app.js開始,但不一樣的事要從app.js的外面著手
app.js移動 至 新資料夾:page =>重新命名為app.jsx

引用自創的全域框架 ContextProvider.jsx
從專案 > src > index.js 開始

/*file index.js*/
import React from 'react';
import ReactDOM from 'react-dom';
import './css/index.scss';//global css
import App  from './page/App';/*app.js移動 至 新資料夾:page =>重新命名為app.jsx*/
import { ContextProvider } from './constant/ContextProvider';
import { BrowserRouter, Route, Routes } from "react-router-dom";
/*Route寫法可能因為版本 引用元件會完全不一樣 但是架構類似*/

ReactDOM.render(
    <React.StrictMode>
        <ContextProvider>
            <BrowserRouter>
                <Routes>
                    <Route path='/' element={<App />} />
                </Routes>
            </BrowserRouter>
        </ContextProvider>
    </React.StrictMode>,
    document.getElementById('root')
);

src > 新增資料夾:constant > 新增檔案:ContextProvider.jsx

/*file ContextProvider.jsx*/
import { createContext, useContext, useState } from "react";
import { redirect } from "react-router-dom";/*這裡轉址 也因為版本import元件不一樣寫法差不多*/

const StateContext = createContext({
    user: null,
    token: null,
    notification: null,
    setUser: () => { },
    setToken: () => { },
    setNotification: () => { },
    setLoading: () => { }
});

export const ContextProvider = ({ children }) => {
    const [user, _setUser] = useState(localStorage.getItem('user') ? JSON.parse(localStorage.getItem('user')) : {});
    const [token, _setToken] = useState(localStorage.getItem('ACCESS_TOKEN'));
    const [notification, _setNotification] = useState('');
    const [isLoading, _setLoading] = useState(true);

    const setUser = (user) => {
        if (user && typeof user === 'object' && user['id']) {
            _setUser(user);
            localStorage.setItem('user', JSON.stringify(user));
        } else if (user === 'logout') {
            _setUser(null);
            localStorage.removeItem('user');
            setToken(null);
            return redirect('/')
        } else {
            _setUser(null);
            localStorage.removeItem('user');
            setToken(null);
            return redirect('/login', {
                onFinish: () => setNotification('登入逾期失效')
            })
        }
    }

    const setToken = (token) => {
        if (token) {
            _setToken(token);
            localStorage.setItem('ACCESS_TOKEN', token);
        } else {
            _setToken(null);
            localStorage.removeItem('ACCESS_TOKEN');
        }
    }

    const setNotification = (message) => {
        _setNotification(message);
        setTimeout(() => {
            _setNotification('');
        }, 5000)
    }
    const setLoading = (isLoading) => {
        if (isLoading) {
            _setLoading(true);
        } else {
            _setLoading(false);
        }
    }

    return (
        <StateContext.Provider value={{
            user,
            setUser,
            token,
            setToken,
            notification,
            setNotification,
            setLoading
        }}>
            {isLoading &&
                <div id="loading" className="d-flex justify-content-center align-items-center">
                    <div className="spinner-border text-primary" role="status">
                        <span className="visually-hidden">Loading...</span>
                    </div>
                </div>
            }

            {notification && <div className="alert alert-box alert-success">{notification}</div>}
            {children}
        </StateContext.Provider>
    )
}
export const userStateContext = () => useContext(StateContext)

補充css外觀

$theme-colors: ( 'primary':#407855 );

@import '~bootstrap/scss/bootstrap';

#loading {
    position: absolute;
    top: 0;
    left: 0;
    min-width: 100vw;
    min-height: 100vh;
    background-color: rgba(111,111,111,0.35);
    z-index: 2090;
    cursor: progress;
}

到這裡基本上可以看到 綠色(#4407855)的按鈕跟loading了
https://ithelp.ithome.com.tw/upload/images/20230907/20158340SAAc2u8RuM.jpg


模仿登入效果

  1. 首頁(/) =>app頁面=> 載入完成隱藏loading
  2. 按下按鈕 模仿登入
  3. 寫入使用者token到背景
  4. 跨頁 (index.js 增加路由, 新增頁面)
  5. 觸發 notification 歡迎登入訊息

app(首頁)

/*file app.jsx*/
import React, { useEffect } from 'react';
import { userStateContext } from "../constant/ContextProvider";
import 'sweetalert2/src/sweetalert2.scss'
import { useNavigate } from "react-router-dom";

function App() {
    const { user, setUser, token, setToken, setLoading, setNotification } = userStateContext();/*引用全域函數*/
    const navigate = useNavigate();
    useEffect(() => {/*初始化*/
        setLoading(false);
    }, []);/*這個矩陣是很重要的*/

    function fakeLogin() {
        setLoading(true);
        setToken('kasnvlawejnflasdfjklfsadnf');
        setUser({ id: '新用戶', email: "dsafsa@afs.com" });
        setNotification('登入成功');
        navigate("about", {
            onFinish: () => setLoading(false)
        });
    }
    return (
        <div className="App">
            {user && Object.keys(user).length && user['id'] ? "歡迎" + user['id'] : null}
            <button className="btn btn-primary" onClick={fakeLogin} >登入</button>
        </div>
    );
}

export default App;

新增路由

/*file: index.js*/
import React from 'react';
import ReactDOM from 'react-dom';
import './css/index.scss';//gloable css
import App from './page/App';
import About from './page/About';/* 引入新頁面*/
import { ContextProvider } from './constant/ContextProvider';
import { BrowserRouter, Route, Routes } from "react-router-dom";

ReactDOM.render(
    <React.StrictMode>
        <ContextProvider>
            <BrowserRouter>
                <Routes>
                    <Route path='/' element={<App />} />
                    <Route path='about' element={<About />} />/* 給予路由 yourDomain/about*/
                </Routes>
            </BrowserRouter>
        </ContextProvider>
    </React.StrictMode>,
    document.getElementById('root')
);

about

新增檔案 src > page > About.jsx

/*file: About.jsx*/
import React, { useEffect } from 'react';
import { userStateContext } from "../constant/ContextProvider";

function About() {
    const { user } = userStateContext();/*引用全域函數*/

    return (
        <div className="App">
            {user && Object.keys(user).length && user['id'] ? "歡迎" + user['id'] : null} 登入
        </div>
    );
}

export default About;

https://ithelp.ithome.com.tw/upload/images/20230908/20158340FbHBECTQoe.jpg

到這邊基本上就可以達成

跨頁面繼承 "狀態", "資料" , "函數"

react其實有點小小缺點,資料只能下向傳承,所以要跨區塊傳遞,會有點小麻煩。
我寫了javascript兩年,沒人教的情況下,有些好用的方法真的會不知道,知道都是抄來的
https://ithelp.ithome.com.tw/upload/images/20230908/20158340vaFPyB0y1P.jpg

以前沒有人可以問,現在有ChatGPT可以問。我的文章不斷要求不使用ChatGPT。
用在ChatGPT一出來,就開始用。用了非常多,而且都是連續問很多問題。(沒辦法上傳gif檔)
我在這裡可以很肯定地說,如果你是要最上面金字塔圖 相鄰兩區塊的問題 已經是複雜度的極限。
ChatGPT對於橫向問題處理很好,但對於1.多系統 2.縱深架構,他會提出的答案,效果可能不夠好。

技術參考來源: 全域資料傳遞:https://www.youtube.com/watch?v=qJq9ZMB2Was&ab_channel=TheCodeholic


上一篇
Ch5. 專案運行,NPM插件,常用插件
下一篇
Ch7. React 呼叫ajax外部API支援
系列文
React前端開發 - 安裝,coding ,架設 - ver. React 17以下14
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言