今天要來嘗試 redux,用的是 RTK(Redux Toolkit),可以先去官網看看這是個甚麼工具。因為第一次要引入近來,有點怕怕的,不然就先來試試做一個簡單一點的功能 :用 redux 來控制彈跳視窗的開合。
先按照官網的指示安裝一下,使用 RTK 總共需要兩個包,一個是 react-redux 另一個是 reduxjs-toolkit :
npm install @reduxjs/toolkit react-redux
接著在 src 底下新增 redux 資料夾,接下來所有 redux 相關的東西都放在這,事不宜遲,馬上先把猶如 redux 靈魂般的 store 新增起來 :
store.ts
import { configureStore } from "@reduxjs/toolkit";
export const store = configureStore({
reducer: {
// 晚點要來放 reducer 的地方
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
接下來安裝在 index.tsx,引入 store 與 Provider,接上他的腿(?) :
index.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import { ChakraProvider } from "@chakra-ui/react";
import { BrowserRouter } from "react-router-dom";
import App from "./App";
import "./index.css";
import { store } from "./redux/store";
import { Provider } from "react-redux";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
<React.StrictMode>
<BrowserRouter>
<Provider store={store}> // 腿在這兒
<ChakraProvider>
<App />
</ChakraProvider>
</Provider>
</BrowserRouter>
</React.StrictMode>
);
為了少寫一點點程式碼,所以來做個 redux 的 hook,稍微包裝一下 dispatch 與 selector :
import { useDispatch, TypedUseSelectorHook, useSelector } from "react-redux";
import type { RootState, AppDispatch } from "./store";
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
好,來新增一個負責 Modal 的 slice,就叫做 modalSlice,在 redux 資料夾底下新增一個 modalSlice,裡面要放兩個檔案,一個是 index.d.ts 用來定義型別,另一個就是本體叫做 modalSlice.ts :
index.d.ts
import { store } from "../store";
export type ModalState = {
isOpen: boolean; // 目前只需要這個 state
};
export type RootState = ReturnType<typeof store.getState>;
modalSlice.ts
import { createSlice } from "@reduxjs/toolkit";
import type { PayloadAction } from "@reduxjs/toolkit";
import { ModalState, RootState } from "./index";
const initialState: ModalState = {
isOpen: false,
};
const modalSlice = createSlice({
name: "modal",
initialState,
reducers: {
openModal: (state: ModalState, action: PayloadAction) => {
state.isOpen = true;
},
closeModal: (state: ModalState, action: PayloadAction) => {
state.isOpen = false;
},
},
});
export const { openModal, closeModal } = modalSlice.actions;
export const selectModalIsOpen = (state: RootState) => state.modalSlice.isOpen;
export default modalSlice.reducer;
做好了 slice,最後把 reducer 放回 store,前置作業就算完成了 :
import { configureStore } from "@reduxjs/toolkit";
import modalSlice from "./modalSlice/modalSlice";
export const store = configureStore({
reducer: {
modalSlice,
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
回到 TodoPage ,等下要在 TodoPage 繼續做事,引入必要的工具 :
...
import { useAppSelector, useAppDispatch } from "../../redux/hooks";
import {
selectModalIsOpen,
openModal,
closeModal,
} from "../../redux/modalSlice/modalSlice";
...
接著抓到剛剛在 modalSlice 裡面設定好的 isModalOpen,取代原本的 state :
...
const isOpen = useAppSelector(selectModalIsOpen);
// const [isOpen, setIsOpen] = useState(false);
...
把 dispatch 給初始化,等一下要 action 還要用它 :
...
const dispatch = useAppDispatch();
...
準備就緒之後,把報錯清掉,一步一步更新 UI, :
...
<TodoModal
isOpen={isOpen}
closeModal={() => dispatch(closeModal())} //這裡
register={register}
onSubmit={onSubmit}
handleSubmit={handleSubmit}
reset={reset}
/>
...
TodoModal 這邊有點改變 :
...
type Props = {
isOpen?;
closeModal; // 增加關窗 prop
onSubmit;
register;
handleSubmit;
reset;
};
...
onClick={closeModal} // 在叉叉上面放著
...
新增 redux 之後程式碼暴增,原本要在 TodoPage 控制跳窗,只需要一點點程式碼而已 XD :
const [isOpen, setIsOpen] = useState(false); // 單靠 setIsOpen 走天下
從新增彈跳視窗這個相對簡單的功能來暖身一下,覺得還滿合適的,我喜歡這樣用這樣的方法在新專案開始 RTK。