iT邦幫忙

2023 iThome 鐵人賽

0
Modern Web

React前端開發 - 安裝,coding ,架設 - ver. React 17以下系列 第 14

Ch.14 React套用全域 參數,函數,style

  • 分享至 

  • xImage
  •  

雖然前面可能已經提過 但現在要同樣的東西搬到專案裡面
一個好的代碼架構 是可以經得起ctrl+C與ctrl+V的考驗
這就是我抄隔壁同學作業的理由

style scc

一些自以常用的css 改成用scss寫
路徑: 專案/resources/sass
_variables.scss 引入scss常用參數
當然也可以在這裡overwrite顏色

// Body
$body-bg: #f8fafc;
$primary: #4b5563;
$danger: #dc3545;
$info:#5899b9;

例如 body的底色, 文字基本屬性

共用的class或標籤 可以統一管理
如果沒有可以跳過此步驟
路徑: 專案/resources/sass/app.scss
基於遠本的預設 我們多import 自訂的theme 來管理style

// Fonts
@import url('https://fonts.bunny.net/css?family=Nunito');

// Variables
@import 'variables';

// Theme
@import 'theme.scss';

// Bootstrap
@import 'bootstrap/scss/bootstrap';

新增檔案 路徑: 專案/resources/sass/theme.scss
之後共用的class或標籤style都可以統一寫在這裡


全域常數,全域函數,環境常數

這部分的解法非常多我們可以用

  1. 後端方式處理
  2. 前端打包時環境參數
  3. 載入寫好定義的js文件
    看各位實際上想怎麼使用
    我們一步步練習

後端處理常數

我們用Larael處理參數
比較嚴謹的方式是先建立處理參數的php檔案,然後再讓Laravel幫我們引用處理
新建檔案(也可沿用已有參數檔案)
兩步驟
一、專案/.env 建立對應參數
我定義 環境常數 "MY_DEFINED_DATA" 他的值是 'qwer'。

...略

MY_DEFINED_DATA='qwer'

二、專案/config/frontend.php 定義引用

<?php
return [
    //此行為備註 '參數名稱' => env('env內紀錄參數名','forge')
    my_defined_data => env('MY_DEFINED_DATA','forge'),
];

之後引用就

{config('定義參數的檔案.參數名稱','預設值(option)')}

之後引用就{config('frontend.my_defined_data')}
但這個語法跟我們react引用太容易搞混,所以我們在jsx通常不太用
但是在app.blade.php (等同於react專案 public底下的index.html)
可以用來載入頁面基本名稱
另外補充
在js檔直接引用.env 會需要呼叫 "${import.meta.env"

const API_URL = `${import.meta.env.VITE_API_BASE_URL}/api`;//這裡我直接引用.env檔的值

我不太確定這是vite node interia誰家的語法

前端打包時環境參數

這方法我自己覺得很方便,不過受限於公司coding規範,不是每次都能過codereview
三步驟:
一、新建目錄Constant與檔案consts.js
專案/resources/Constant/consts.js

import { createContext, useContext, useState } from "react";
import { router } from '@inertiajs/react';
/*引用區塊*/
const StateContext = createContext({
    user: null,
    token: null,
    notification: null,
    setUser: () => { },
    setToken: () => { },
    setNotification: () => { },
    setLoading: () => { }
    env_data:'常數數值' //定義一個常數叫做'env_data'他的值是'常數數值'
});
/*引用區塊 end*/
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 router.visit('/')
        }else{
            _setUser(null);
            localStorage.removeItem('user');
            setToken(null);
            return router.visit('/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)

二、引用該檔案至全域
專案/resources/app.jsx

...
import { ContextProvider } from './Constant/ContextProvider';
/*註 import要放在開頭區塊,不要放在中間才引用然後說沒用,謝謝*/
...


...
/*用ContestProvider 包住底下的 App*/
        <ContextProvider>
            <App {...props} />
        </ContextProvider>
...

三、各別檔案使用方法
要使用環境參數的頁面 某個.jsx
開頭import userStateContext
然後在function裡面引用

...
import { userStateContext } from "@/Constant/ContextProvider";

... function ...{
//可以挑選 引用區塊內的任何函數或環境參數
const { env_data } = userStateContext()
...

載入寫好定義的js文件

我通常是在API文件使用,因為像是header常常是固定的開頭
新建檔案
一、新建目錄API與檔案consts.js
專案/resources/API/consts.js

import axios from 'axios';

export const getHeaders = (isUseToken = true) => {
    //我的api不是每支都要用token 會寫篩選動作
    var token = '';
    if (isUseToken) {
        try {
            token = localStorage.getItem('ACCESS_TOKEN');
            //這是基於我的專案資料放在localStorage,你的放在哪就去哪裡抓
            if (!token) {
                token = ''
            }
        } catch (e) {
            token = '';
        }
    }

    return {
        'Content-Type': 'application/json;chartset=utf-8',
        'Access-Control-Allow-Origin': '*',
        'Accept': 'application/json',
        //'Accept-Encoding':"gzip, deflate, br",
        'Authorization': isUseToken ? `bearer ${token}` : null, //我的案件是用bearerToken
    }
}

const API_URL = `${import.meta.env.VITE_API_BASE_URL}/api`;//這裡我直接引用.env檔的值

const baseRequest = axios.create({
    baseURL: API_URL,
    timeout: 10000
});

//登入
export const API_POST_Login = data => baseRequest.post('/login', JSON.stringify(data), { data: data, headers: getHeaders(false) });

各別.jsx檔案引用

import { API_POST_Login } from "@/API/constants";

//直接當作function使用
API_POST_Login(連線body)
    //這裡可以用axios promise接收語法替換掉 看你的需求
    .then((response)=>{
        //成功
    }).catch(err=>{
        //失敗
    }).finally(()=>{
        //不論如何都要
    })

這邊還有一點可以優化的是"ACCESS_TOKEN"本身也要改成用環境名稱引用。
你如果專案是保持預設,原本是引用bootstrap.js檔案去處理axios,我覺得命名跟我用的bootsrap套件重複命名,所以就不用他,所以新建檔案複製其功能


上一篇
Ch.13 混和架構所以參數散落在檔案各處
系列文
React前端開發 - 安裝,coding ,架設 - ver. React 17以下14
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言