前面的章節我們完成了完整的商品進貨購買功能,是時候算個帳了!
今天就是要把錢的部分給加進來,我們可以從先前的 Redux Toolkit 的專案內搬移 assetsSlice 進來,所以我們一樣於 src/features/slices 路徑下新增一個 assetsSlice.js 的檔案,將原本的內容做以下修改:
// src/features/slices/assetsSlice.js
import { createSlice } from "@reduxjs/toolkit";
import { cakeOrdered, cakeRestocked } from "./cakeSlice";
import { coffeeBeanOrdered, coffeeBeanRestocked } from "./coffeeBeanSlice";
import { coffeeOrdered, coffeeRestocked } from "./coffeeSlice";
const initialState = {
money: 1000,
}
const assetsSlice = createSlice({
name: 'assets',
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(coffeeOrdered, (state, action) => {
state.money = state.money + parseInt(action.payload.money)
return state;
})
.addCase(coffeeRestocked, (state, action) => {
state.money = state.money - parseInt(action.payload.money)
return state;
})
.addCase(coffeeBeanOrdered, (state, action) => {
state.money = state.money + parseInt(action.payload.money)
return state;
})
.addCase(coffeeBeanRestocked, (state, action) => {
state.money = state.money - parseInt(action.payload.money)
return state;
})
.addCase(cakeOrdered, (state, action) => {
state.money = state.money + parseInt(action.payload.money)
return state;
})
.addCase(cakeRestocked, (state, action) => {
state.money = state.money - parseInt(action.payload.money)
return state;
})
}
})
// 方便辨識的處理
export const selectAssets = (state) => state.assets;
export default assetsSlice.reducer
完成後不要忘記調整 store,如下:
// src/features/store.js
import { configureStore } from '@reduxjs/toolkit'
import assetsSlice from './slices/assetsSlice'
import cakeSlice from './slices/cakeSlice'
import coffeeBeanSlice from './slices/coffeeBeanSlice'
import coffeeSlice from './slices/coffeeSlice'
const store = configureStore({
reducer: {
coffee: coffeeSlice,
coffeeBean: coffeeBeanSlice,
cake: cakeSlice,
assets: assetsSlice
}
})
export default store
到這裡基本的功能已經完成了,由於資金的部分都是依附在其他動作上的連動,所以畫面上基本沒有什麼需要做到的調整,但為了整齊我還是會在 components 裏面新增一個 component 用來單純顯示目前的資金狀況:
// src/components/AssetsBlock.jsx
import React from 'react'
import { useSelector } from 'react-redux'
import { selectAssets } from '../features/slices/assetsSlice'
const AssetsBlock = () => {
const assets = useSelector(selectAssets)
return (
<div>
<h4>資金 {assets.money}</h4>
</div>
)
}
export default AssetsBlock
然後將剛剛做好的 component 引入 App 顯示:
// src/App.jsx
import AssetsBlock from "./components/AssetsBlock"
import CakeBlock from "./components/CakeBlock"
import CoffeeBeanBlock from "./components/CoffeeBeanBlock"
import CoffeeBlock from "./components/CoffeeBlock"
function App() {
return (
<div className="container">
<h1>Restaurant Record</h1>
<AssetsBlock/>
<CoffeeBlock/>
<CoffeeBeanBlock/>
<CakeBlock/>
</div>
)
}
export default App
此時可以測試看看資金的數值是否有根據其他的動作而改變,那麼接著我們要阻止一件事是如果欄位為空時要補數字0,不然會造成 NaN 的囧況,所以我們回到每個 component 裏面修正一下送出的 function,讓 null 轉成 0,我就以 CoffeeBlock 為例修正:
// 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 || 0,
money: coffeeOrderMoneyRef.current.value || 0
}
dispatch(coffeeOrdered(sendData))
}
// 進貨送出
const doRestockCoffee = () => {
const sendData = {
qty: coffeeRestockQtyRef.current.value || 0,
money: coffeeRestockMoneyRef.current.value || 0
}
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
其餘的 component 也做一樣的修正後應該能解決 NaN 造成的小 bug。
那麼,今天的內容就到這,下一篇我們來處理 pokemon 的搬移。