此篇主要說明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
接下來我們就直接用計數器範例來說明各元素的功能以及作用
開始前先用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
-------------------------------------------------------------------------------------------------------------------
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 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 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 { 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...等等,後續如果有用到時再分享給大家,謝謝!
沒有留言:
張貼留言