iT邦幫忙

2023 iThome 鐵人賽

DAY 25
0
Modern Web

從Vue學React!不只要會用,還要真的懂~系列 第 25

【Day 25】用實作學Redux Toolkit!完成一個購物車(上) - 基本設定&基本用法

  • 分享至 

  • xImage
  •  

昨天已經先認識了Vuex和Redux這兩個狀態管理的工具了!今天則是會專注看怎麼把Redux使用到React上的部分。當我們想要在React上使用Redux時,雖然可以單獨使用Redux就好,但是為了讓Redux能和React好好地被綁定在一起,我們會使用React-Redux,為了讓寫法及用法更簡潔方便,還可以與Redux Toolkit一起使用。今天我們就會從認識React-Redux和Redux Toolkit開始學習怎麼把Redux使用在React上。

用它們之前先認識它們!React-Redux和Redux Toolkit是什麼?

「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和Redux Toolkit

可以透過以下指令把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。

從建立store開始正式使用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的物件。
https://ithelp.ithome.com.tw/upload/images/20230930/20130914yCWakz79JR.png

  • reducer: 是一個 reducer 函式,其中包含了處理不同 action 的邏輯,帶入configureStore函式,就會以這個reducer管理slice的狀態。
  • actions: 這是一個物件,包含自動生成的action creators,每個 action creator可以用來發送對應的action來進行store裡state的變更。
  • caseReducers: caseReducers是一個物件,內含每個action的case reducer,case reducer會接收state和action當作參數,然後回傳一個處理過的state,這些reducer函式會在reducer中被組合使用。
  • getInitialState:這個函式會回傳初始的state。

接著把創建slice回傳的reducer帶入configureStore,就能創建store。

import { configureStore } from "@reduxjs/toolkit";
import shoppingCartReducer from './slices/shoppingCart';

const store = configureStore({
  reducer: {
    shoppingCartReducer,
  }
});

從創建的store,可以取得dispatch函式,這是之後進行state操作會使用到的函式。

把store套用到全域環境

把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>
);

透過action更新state

已經把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));
  };
 // 略
}

取得store裡的state

把商品加入購物車的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>
  )
}

類似getter!建立處理過後的state並取得

不只可以透過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完成「加商品到購物車」、「顯示購物車商品」、「顯示購物車總金額」這幾個功能。
https://i.imgur.com/87P5PHZ.gif

回顧&總結

在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裡面進行非同步的操作,明天就會接著來看這個部分!


上一篇
【Day 24】跨越直系與旁系元件的全域狀態管理工具
下一篇
【Day 26】用實作學Redux Toolkit!完成一個購物車(下) - Middleware之處理非同步的操作
系列文
從Vue學React!不只要會用,還要真的懂~30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言