昨天已經先認識了Vuex和Redux這兩個狀態管理的工具了!今天則是會專注看怎麼把Redux使用到React上的部分。當我們想要在React上使用Redux時,雖然可以單獨使用Redux就好,但是為了讓Redux能和React好好地被綁定在一起,我們會使用React-Redux,為了讓寫法及用法更簡潔方便,還可以與Redux Toolkit一起使用。今天我們就會從認識React-Redux和Redux Toolkit開始學習怎麼把Redux使用在React上。
「React-Redux」是一個 UI binding Library
,在React中使用Redux的時候,會需要搭配React-Redux。雖然Redux是一個獨立的狀態管理Library,不僅能在React中使用,還能在Vue、Angular等前端框架上使用,但是並不會直接將前端畫面跟它綁定在一起,所以在使用Redux時,如果沒有使用UI binding Library把Redux和前端畫面綁定在一起,就會需要額外透過手動的方式處理state更新後,反映到UI更新上的部分,而這將會使得程式碼變得非常複雜難以管理。如果在React中使用Redux時,有和React-Redux一起使用,則能讓state有更新時,可以自動反映到UI上。
「Redux Toolkit」是官方推薦與Redux一起使用的Library,簡稱RTK(後面也會統一使用RTK這個簡稱),主要用途是為了簡化使用Redux時,所需要寫的程式碼及步驟
,讓大家在使用Redux時,能更快速、輕鬆地建構及維護全域狀態。在使用Redux時,如果搭配RTK使用的話,就不需要手動寫action type,reducer也不需要透過switch的方式寫,整體的程式碼也就能變得更簡潔。這次主要會搭配RTK來使用Redux,所以接下來就會來好好地看看搭配RTK來使用Redux的用法。
由於如果以不連貫的程式碼來看Redux的使用,可能會有點難進入狀況,所以在接下來熟悉如何使用Redux的篇章內容,會以實作一個簡易的「購物車」的例子來看Redux的用法。
可以透過以下指令把Redux安裝到自己的專案中。
React-Redux
# NPM
npm install react-redux
#Yarn
yarn add react-redux
Redux Toolkit
# NPM
npm install @reduxjs/toolkit
# Yarn
yarn add @reduxjs/toolkit
安裝完Redux Toolkit就會包含Redux,所以不需要額外再安裝Redux。
已經安裝好RTK,可以來正式使用Redux了,現在就從建立store開始吧!
建立store的時候需要使用configureStore
,還需要把reducer帶入,所以在這個步驟也會把reducer一起建立好。
RTK的reducer是使用createSlice
來建立,可以在建立slice的時候定義這個slice的名稱、帶入初始state,以及定義處理各種action的reducer函式
。
import { createSlice } from "@reduxjs/toolkit";
export const todoSlice = createSlice({
name: 'todo',
initialState,
reducers: {
addTodoItem: (state, action) => {
state.todoList.push(action.payload);
},
}
});
export default shoppingCartSlice.reducer;
透過createSlice創建的slice會回傳內含actions、reducer、caseReducers、getInitialState的物件。
接著把創建slice回傳的reducer帶入configureStore,就能創建store。
import { configureStore } from "@reduxjs/toolkit";
import shoppingCartReducer from './slices/shoppingCart';
const store = configureStore({
reducer: {
shoppingCartReducer,
}
});
從創建的store,可以取得dispatch函式
,這是之後進行state操作會使用到的函式。
把store建立好之後,透過react-redux的Provider就可以把建立完畢的store帶到全域環境。
import { Provider } from 'react-redux';
import store from './store';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
// 透過Provider把store帶到全域環境
<Provider store={ store }>
<App />
</Provider>
</React.StrictMode>
);
已經把store套用到專案中了,這時候就可以來試試看使用action來更新state了。
先從slice的檔案export出我們建立的slice中的actions。
import { createSlice } from "@reduxjs/toolkit";
// 略
export const { addProductItem } = shoppingCartSlice.actions;
export default shoppingCartSlice.reducer;
在要使用的地方import要用的action,以及useDispactch。使用useDispatch,會回傳發送action進行state更新的dispatch函式。
import { useDispatch } from "react-redux";
import { addProductItem } from "../slices/shoppingCart";
export default function ProductList() {
// 取得dispatch函式
const dispatch = useDispatch();
const addProductToCart = (payload) => {
// 用dispatch函式進行state的更新
dispatch(addProductItem(payload));
};
// 略
}
把商品加入購物車的state之後,想要取得被保管在store內的state到畫面上顯示,可以使用useSelector
這個hook。
import CartItem from "./CartItem";
import { useSelector } from "react-redux";
export default function Cart() {
// 透過useSelector取得cartItemList
const { cartItemList } = useSelector(state => state.shoppingCartReducer);
return (
<div className="cart">
<h2>Shopping Cart</h2>
<div className="cart-item-container">
{
cartItemList.map((cartItem) => (
<CartItem cartItem={cartItem} key={cartItem.id} />
))
}
</div>
<div className="total-amount">
Total Amount:
<span className="amount"> 300</span>
</div>
</div>
)
}
不只可以透過useSelector來快速取得store裡面的state,還可以在store裡面先透過createSelector
來處理一些我們畫面上會用到的計算過的值,再透過useSelector取得那個處理過後的值,也就可以把一些邏輯集中寫在slice裡面。
前面已經實作透過useSelector取得store裡面的cartItemList,我們再來透過createSelector來計算購物車裡商品的總金額。createSelector會放在createSlice的那個檔案,也就是shoppingCart.js的那個檔案裡面。
import { createSlice } from "@reduxjs/toolkit";
// 略
// 可以取得cartItemList的函式
const selectCartItemList = (state) => state.shoppingCartReducer.cartItemList;
// 進行總金額的計算
export const totalCartItemPriceSelector = createSelector(selectCartItemList, (selectCartItemList) => selectCartItemList.reduce((total, item) => total + item.price * item.amount, 0));
上面的步驟完成後,就可以import到cart裡面使用。
import CartItem from "./CartItem";
import { useSelector } from "react-redux";
import { totalCartItemPriceSelector } from "../slices/shoppingCart";
export default function Cart() {
const { cartItemList } = useSelector(state => state.shoppingCartReducer);
// 這裡就可以透過useSelector取得createSelector計算的總金額
const totalCartItemPrice = useSelector(totalCartItemPriceSelector);
return (
<div className="cart">
// 略
<div className="total-amount">
Total Amount:
<span className="amount"> {totalCartItemPrice}</span>
</div>
</div>
)
}
到目前為止,已經結合toolkit完成「加商品到購物車」、「顯示購物車商品」、「顯示購物車總金額」這幾個功能。
在React使用Redux的時候,雖然可以直接使用Redux,但如果想要把綁Redux管理的state綁定到React的UI上,就會需要額外寫一些程式碼來做相對應的處理。所以為了更便利的使用Redux,通常會透過「React-Redux」這個UI binding Library來把Redux和React UI綁定在一起
。
「Redux Toolkit」則是官方推薦用來和Redux一起使用的Library,使用Redux Toolkit可以簡化使用Redux時,所需要寫的程式碼及步驟,讓使用Redux時,變得更容易
。
除了認識React-Redux和Redux Toolkit外,今天還看了Redux Toolkit的一些基本的用法,像是建立store、更新store裡的state,以及取得store的state,還有類似Vuex getter用法的createSeletor。除了這些用法外,還有一個很重要的部分還沒有提到,那就是如何在Redux裡面進行非同步的操作,明天就會接著來看這個部分!