2023年1月30日 星期一

[React]實作自定義的Component(密碼輸入框)

 


這篇主要說明如何開發自定義的Component,讓多個頁面都可同時使用,範例程式碼可參考這裡


最常見的自定義元件大概就是輸入框、讀取動畫或視窗...這類的。因為之前有實作過登入頁,那就以密碼輸入框來教大家如何實作。

因示範專案使用的是MUI,建議各位在開發任何元件前都可確認有沒有官方範例,有的話就可以省下大量的時間去實作。

而這功能確實有找到官方範例,那就可以直接抓程式碼下來使用!


首先先在src/components/text裡新增Password.tsx,並將官方範例貼上去。

Password.tsx

----------------------------------------------------------------------------------------------------------------

import { Visibility, VisibilityOff } from "@mui/icons-material";
import { IconButton, InputAdornment, TextField } from "@mui/material";
import { useState } from "react";
export default function Password(props: passwordProps) {
    //顯示密碼Flag
    const [showPassword, setShowPassword] = useState(false);
    //眼睛點選事件
    const handleClickShowPassword = () => setShowPassword((show) => !show);
    //取消預設點選事件
    const handleMouseDownPassword = (event: React.MouseEvent<HTMLButtonElement>) => {
        event.preventDefault();
    };
    return (
        <TextField
            error={props.ErrorMsg !== ''}
            margin="normal"
            required
            fullWidth
            name={props.Name}//"password"
            label={props.Label}//"Password"
            id={props.ID}//"password"
            autoComplete="current-password"
            helperText={props.ErrorMsg !== '' ? props.ErrorMsg : ""}
            InputProps={{
                type: showPassword ? 'text' : 'password',
                endAdornment:
                    <InputAdornment position="end" >
                        <IconButton
                            aria-label="toggle password visibility"
                            onClick={handleClickShowPassword}
                            onMouseDown={handleMouseDownPassword}
                            edge="end"
                        >
                            {showPassword ? <VisibilityOff /> : <Visibility />}
                        </IconButton>
                    </InputAdornment>
            }
            }
        />
    );
}
interface passwordProps {
    ID:string
    Name: string
    Label:string
    ErrorMsg?: string;
}

----------------------------------------------------------------------------------------------------------------


主要定義幾個屬性

ID:元件ID。
Name:元件Name,有包Form的可透過FormData.get({name})抓到值。
Label:元件Label。
ErrorMsg:顯示錯誤訊息。 

 

再將Login的原先密碼欄位改成使用<Password/>

Login.tsx

----------------------------------------------------------------------------------------------------------------

import Password from '../components/text/Password';

省略...

{/*原程式碼*/}
{/*<TextField*/}
{/*    error={LoginMsg !== ''}*/}
{/*    margin="normal"*/}
{/*    required*/}
{/*    fullWidth*/}
{/*    name="password"*/}
{/*    label={t('Login.Password')}*/}
{/*    type="password"*/}
{/*    id="password"*/}
{/*    autoComplete="current-password"*/}
{/*    helperText={LoginMsg !== '' ? LoginMsg:""}*/}
{/*/>*/}
 <Password ID="password" Name="password" Label={t('Login.Password')} ErrorMsg={LoginMsg} />

省略...

----------------------------------------------------------------------------------------------------------------


這樣就完成囉,未來如果有需要使用密碼欄位就可以同Login頁載入使用,下面Demo一下效果

初始為不顯示密碼

點選眼睛,就可以看到剛輸入的密碼。


看起來功能正常,這樣大家就可以開始來製作屬於自己的元件囉!


這篇功能感覺比前幾篇簡單許多,其實是各位在之前的教學都已經有作過了,讓大家猜猜看是什麼?


公布答案!其實各個頁面也是Component,只是載入的地方在APP.tsx內且透過React-Router去切換掉Content,所以如果有多個地方要顯示同樣的頁面,也可以使用此篇教的方法囉!















2023年1月4日 星期三

[React]Redux說明&導入

 

此篇主要說明Redux,範例程式碼可參考這裡


基本上Redux的功用就是管理狀態以及畫面與資料分開管理,與之前加上登入機制所使用的Context功能大同小異,優於Context的地方是在有多個state要管理時會優於React原生的Context。

p.s. Redux和Context API可混用,需頻繁操作的state可以用Redux,反之則用Context。

主要核心元件為redux和react-redux,redux是函式庫本體,react-redux則是React綁定使用套件。

Redux組成主要有三個元素

1.action

定義操作state方法,會傳入reducer。

2.reducer

保管State並針對傳入的action去對state做動作。

3.store

管理並整合reducer。

以下為Redux 的流程示意圖


接下來我們就直接用計數器範例來說明各元素的功能以及作用


開始前先用npm安裝套件語法如下

//安裝redux和react-redux

npm install --save redux react-redux


//安裝react-redux的Typescript套件

npm install --save --dev @types/react-redux


程式碼部分我們先建立reducer部分,新增reducers資料夾和counter檔案(src/reducers/counter.ts)

counter.ts裡面有Action類別定義、Actions和reducer本體,程式碼如下


counter.ts

-------------------------------------------------------------------------------------------------------------------

//初始state
const initState = {
    count: 0
};

// #region 定義Action類別
//增加
const INCREMENT = 'INCREMENT';
//減少
const DECREMENT = 'DECREMENT';
// #endregion 

// #region 匯出Action
//增加
export const countPlus = { type: INCREMENT }
//減少
export const countMinus = { type: DECREMENT }
// #endregion

// #region reducer本體
const counterReducer = (state = initState, action: any) => {
    //依照action的類型作判斷
    switch (action.type) {
        case INCREMENT:
            return { count: state.count + 1 };
        case DECREMENT:
            return { count: state.count - 1 };
        default:
            return state;
    }
};
// #endergion 

export default counterReducer;

-------------------------------------------------------------------------------------------------------------------

簡單說明,定義以及匯出Action主要是集中管理狀態的處理,對後續維護以及測試有許多幫助。

而reducer本體則是依照傳入的action去操作state並回傳最新的State,使之更新view。


再來建立集中管理reducer的store,新增stores資料夾和configureStore檔案(src/stores/configureStore.ts)


configureStore.ts

-------------------------------------------------------------------------------------------------------------------

import { createStore, combineReducers } from 'redux';
import counterReducer from '../reducers/counter';
const rootReducer = combineReducers({
    counterReducer,
});
const store = createStore(rootReducer);
export default store;

-------------------------------------------------------------------------------------------------------------------

如果只有一個reducer的可以直接createStore,但預想未來專案可能會有多個reducer,所以中間多一個combineReducers,有新增時在填入。


再來要修改index.tsx讓所有Component可以使用,需要使用react-redux的Provider來包覆住APP。


//index.tsx(紅字部分為新增)

-------------------------------------------------------------------------------------------------------------------

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import './i18n';
import { Provider } from 'react-redux';
import store from './stores/configureStore';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
    <React.StrictMode>
        <Provider store={store}>
            <App />
        </Provider>
  </React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals(console.log);

-------------------------------------------------------------------------------------------------------------------

透過Provider的store讓全系統都可使用Redux。


前面這樣定義就完成了,開始撰寫計數器,新增counter.tsx頁面

//counter.tsx

-------------------------------------------------------------------------------------------------------------------

import * as React from 'react';
import { useSelector,useDispatch } from 'react-redux';
import { countMinus, countPlus } from '../reducers/counter';
export function Counter() {
    
    //透過useDispatch來使用action
    const dispatch = useDispatch();
    //透過useSelector去取得State
    const count: number = useSelector(state => (state as any).counterReducer.count);
    return (
        <React.Fragment>
            <h1>Counter</h1>
            <h3> Redux</h3>
            <p aria-live="polite">Current count: <strong>{count}</strong></p>
            <button style={{ margin: '5px' }}
                type="button"
                className="btn btn-primary btn-lg"
                onClick={() => { dispatch(countPlus); }}>
                Increment
            </button>
            <button style={{ margin: '5px' }}
                type="button"
                className="btn btn-primary btn-lg"
                onClick={() => { dispatch(countMinus); }}>
                Decrement
            </button>
        </React.Fragment>
    );
}

-------------------------------------------------------------------------------------------------------------------

說明一下程式碼,

操作部分使用useDispatch來取得dispatch,在按鈕時在使用dispatch傳入定義好的action操作state。

而畫面更新部分則是使用useSelector取代useState,如果state更新則有使用到useSelector的Component也會更新。


實際就可以來Run看看畫面,看起來是可以正常加減。



p.s.因為是全域的state,所以要注意如果切頁再回來值不會變回0。


這樣就完成了資料與畫面分離的部分,對於未來的維護上應該也就簡單許多,建議大家還是要使用Redux。


一樣在結尾也說明一下其實還有更多的應用沒有一起說明,例如操作資料庫或是用redux-logger來記錄每次操作state...等等,後續如果有用到時再分享給大家,謝謝!










【.Net Core】 EF Core + Web API 實作

 EF Core是Entity Framework在.Net Core使用的版本,功能幾乎相同,但具有輕巧、高擴充性以及高效能等優點,建議各位學習。 通常在.Net Core如果要用ORM的方式會有兩種選擇分別是EF Core以及Dapper。 從其他網路文章可以看到這兩種在最新...