iT邦幫忙

2022 iThome 鐵人賽

DAY 20
0
自我挑戰組

30天深入淺出Redux系列 第 20

Redux 深入淺出 - [ Day 20 ] React Redux 商品功能設定-(咖啡&咖啡豆)

  • 分享至 

  • xImage
  •  

前一篇,我們已經完成了咖啡商品的購買功能,以及示範了 React 的 component 拆分方法,那麼這篇我們就接續完成另一項進貨的功能吧!

https://ithelp.ithome.com.tw/upload/images/20220930/20129020PdveWsModY.png

首先,我們先回到上一篇我們修改並拆分出來的 CoffeeBlock component 裏面做以下修改:

// src/components/CoffeeBlock.jsx
import React, { useRef } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { coffeeOrdered, coffeeRestocked, selectCoffee } from '../features/slices/coffeeSlice'

const CoffeeBlock = () => {
  const coffee = useSelector(selectCoffee)
  const dispatch = useDispatch()
  // 這裡採 react 簡單的 useRef 來處理
  // 購買
  const coffeeOrderQtyRef = useRef(null)
  const coffeeOrderMoneyRef = useRef(null)
  // 進貨
  const coffeeRestockQtyRef = useRef(null)
  const coffeeRestockMoneyRef = useRef(null)
  // 購買送出
  const doOrderCoffee = () => {
    const sendData = {
      qty: coffeeOrderQtyRef.current.value,
      money: coffeeOrderMoneyRef.current.value
    }
    dispatch(coffeeOrdered(sendData))
  }
  // 進貨送出
  const doRestockCoffee = () => {

    const sendData = {
      qty: coffeeRestockQtyRef.current.value,
      money: coffeeRestockMoneyRef.current.value
    }
    dispatch(coffeeRestocked(sendData))
  }
  console.log('coffee', coffee);
  return (
    <div>
      <h4>咖啡存量 {coffee.numOfCoffee}</h4>
      <fieldset>
        <legend>咖啡購買</legend>
        <div>
          <label htmlFor="coffeeOrderQty">數量</label>
          <input id="coffeeOrderQty" type="number" ref={coffeeOrderQtyRef} />
        </div>
        <div>
          <label htmlFor="coffeeOrderMoney">價錢</label>
          <input id="coffeeOrderMoney" type="number" ref={coffeeOrderMoneyRef} />
        </div>
        <button onClick={doOrderCoffee}>
          購買
        </button>
      </fieldset>
      <fieldset>
        <legend>咖啡進貨</legend>
        <div>
          <label htmlFor="coffeeRestockQty">數量</label>
          <input id="coffeeRestockQty" type="number" ref={coffeeRestockQtyRef} />
        </div>
        <div>
          <label htmlFor="coffeeRestockMoney">價錢</label>
          <input id="coffeeRestockMoney" type="number" ref={coffeeRestockMoneyRef} />
        </div>
        <button onClick={doRestockCoffee}>
          進貨
        </button>
      </fieldset>
    </div>
  )
}

export default CoffeeBlock

這時,有可能會遇到 type 不同的問題,但為了避免此情況發生影響功能,讓我們回到原本的 coffeeSlice.js 裏面針對我們送出的值做以下轉換修改:

// src/featuers/slices/coffeeSlice.js
import { createSlice } from "@reduxjs/toolkit";

const initialState = {
  numOfCoffee: 20
}

const coffeeSlice = createSlice({
  name: 'coffee',
  initialState,
  reducers: {
    // 這裡避免傳入值為字串進而影響功能,所以加了 parseInt
    coffeeOrdered: (state, action) => {
      state.numOfCoffee = state.numOfCoffee - parseInt(action.payload.qty)
      return state;
    },
    coffeeRestocked: (state, action) => {
      state.numOfCoffee = state.numOfCoffee + parseInt(action.payload.qty)
      return state;
    },
  },
})

// 方便辨識的處理
export const selectCoffee = (state) => state.coffee;

export const { coffeeOrdered, coffeeRestocked } = coffeeSlice.actions

export default coffeeSlice.reducer

這也是為何 TypeScript 會如此受歡迎的原因,在 js 的環境開發常常就會因為這些型別造成很多小 bug,因為在定義的時候通常不會硬性要求去限定型別,但這篇的目的是希望新手也能跟著做,所以大家只要注意這邊的地方要做以上處理即可。

至此,運行上應該就沒有問題了,那麼畫面的話我們還缺少了咖啡豆、蛋糕的部分,接著處理吧!

一樣從 slice 開始搬遷,於 src/features/slices 的路徑下新增 coffeeBeanSlice.js,完成後就開始搬遷如以下:

// src/features/slices/coffeeBeanSlice.js
import { createSlice } from "@reduxjs/toolkit";

const initialState = {
  numOfCoffeeBean: 20
}

const coffeeBeanSlice = createSlice({
  name: 'coffeeBean',
  initialState,
  reducers: {
    // 一樣是為了避棉型別造成的問題
    coffeeBeanOrdered: (state, action) => {
      state.numOfCoffeeBean = state.numOfCoffeeBean - parseInt(action.payload.qty)
      return state;
    },
    coffeeBeanRestocked: (state, action) => {
      state.numOfCoffeeBean = state.numOfCoffeeBean + parseInt(action.payload.qty)
      return state;
    },
  }
})

// 方便辨識的處理
export const selectCoffeeBean = (state) => state.coffeeBean;

export const { coffeeBeanOrdered, coffeeBeanRestocked } = coffeeBeanSlice.actions

export default coffeeBeanSlice.reducer

完成後不要忘記要調整 store 將剛才的 slice 給加進去:

// src/featuers/store.js
import { configureStore } from '@reduxjs/toolkit'
import coffeeBeanSlice from './slices/coffeeBeanSlice'
import coffeeSlice from './slices/coffeeSlice'

const store = configureStore({
  reducer: {
    coffee: coffeeSlice,
    coffeeBean: coffeeBeanSlice
  }
})

export default store

與之前的 coffeeBlock 相同的概念,我們直接於 components 下新增一個 coffeeBeanBlock.jsx 的檔案,用來製作新的 component,如以下:

// src/components/CoffeeBeanBlock.jsx
import React, { useRef } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { coffeeBeanOrdered, coffeeBeanRestocked, selectCoffeeBean } from '../features/slices/coffeeBeanSlice'

const CoffeeBeanBlock = () => {
  const coffeeBean = useSelector(selectCoffeeBean)
  const dispatch = useDispatch()
  // 購買
  const coffeeBeanOrderQtyRef = useRef(null)
  const coffeeBeanOrderMoneyRef = useRef(null)
  // 進貨
  const coffeeBeanRestockQtyRef = useRef(null)
  const coffeeBeanRestockMoneyRef = useRef(null)
  // 購買送出
  const doOrderCoffeeBean = () => {
    const sendData = {
      qty: coffeeBeanOrderQtyRef.current.value,
      money: coffeeBeanOrderMoneyRef.current.value
    }
    dispatch(coffeeBeanOrdered(sendData))
  }
  // 進貨送出
  const doRestockCoffeeBean = () => {

    const sendData = {
      qty: coffeeBeanRestockQtyRef.current.value,
      money: coffeeBeanRestockMoneyRef.current.value
    }
    dispatch(coffeeBeanRestocked(sendData))
  }
  console.log('咖啡豆', coffeeBean);

  return (
    <div>
      <h4>咖啡豆存量 {coffeeBean.numOfCoffeeBean}</h4>
      <fieldset>
        <legend>咖啡豆購買</legend>
        <div>
          <label htmlFor="coffeeBeanOrderQty">數量</label>
          <input id="coffeeBeanOrderQty" type="number" ref={coffeeBeanOrderQtyRef} />
        </div>
        <div>
          <label htmlFor="coffeeBeanOrderMoney">價錢</label>
          <input id="coffeeBeanOrderMoney" type="number" ref={coffeeBeanOrderMoneyRef} />
        </div>
        <button onClick={doOrderCoffeeBean}>
          購買
        </button>
      </fieldset>
      <fieldset>
        <legend>咖啡豆進貨</legend>
        <div>
          <label htmlFor="coffeeBeanRestockQty">數量</label>
          <input id="coffeeBeanRestockQty" type="number" ref={coffeeBeanRestockQtyRef} />
        </div>
        <div>
          <label htmlFor="coffeeBeanRestockMoney">價錢</label>
          <input id="coffeeBeanRestockMoney" type="number" ref={coffeeBeanRestockMoneyRef} />
        </div>
        <button onClick={doRestockCoffeeBean}>
          進貨
        </button>
      </fieldset>
    </div>
  )
}

export default CoffeeBeanBlock

接著我們於 App 內引入我們剛才的段落,如下:

import CoffeeBeanBlock from "./components/CoffeeBeanBlock"
import CoffeeBlock from "./components/CoffeeBlock"

function App() {

  return (
    <div className="container">
      <h1>Restaurant Record</h1>
      <CoffeeBlock/>
      <CoffeeBeanBlock/>
    </div>
  )
}

export default App

此時,於畫面上應該會有四個功能,分別為咖啡購買、咖啡進貨、咖啡豆購買、咖啡豆進貨,功能也是正常的,數量有填寫的話,送出也能使的庫存增減。

那麼今天的分享就到這裡,下一篇我們將蛋糕也搬錢過來吧!


上一篇
Redux 深入淺出 - [ Day 19 ] React Redux setup
下一篇
Redux 深入淺出 - [ Day 21 ] React Redux 商品功能設定 - (蛋糕)
系列文
30天深入淺出Redux31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言