この記事は書籍「TypeScriptとReact/Next.jsでつくる 実践Webアプリケーション開発」に関するものです。
TypeScriptとReact/Next.jsでつくる 実践Webアプリケーション開発のアプリで「カートから削除」ボタンを押すとカートに入っているすべての商品が削除されてしまいます。
removeProductFromCart関数の不具合修正のように修正すると所望の動作になります。
以下詳細です。
カートへの商品追加と削除は以下のようにReducerを使って実装されています。
/**
* ショッピングカートのReducer
* @param state 現在の状態
* @param action アクション
* @returns 次の状態
*/
export const shopReducer: React.Reducer<Product[], ShopReducerAction> = (
state: Product[],
action: ShopReducerAction,
) => {
switch (action.type) {
case ADD_PRODUCT:
return addProductToCart(action.payload, state)
case REMOVE_PRODUCT:
return removeProductFromCart(action.payload, state)
default:
return state
}
}
カートから商品を削除するremoveProductFromCartの実装は以下のようになっています。
/**
* 商品削除アクション
* @param product 商品
* @param state 現在の状態
* @returns 次の状態
*/
const removeProductFromCart = (productId: number, state: Product[]) => {
const removedItemIndex = state.findIndex((item) => item.id === productId)
state.splice(removedItemIndex, 1)
return [...state]
}
useReducer – Reactによると、
In Strict Mode, React will call your reducer and initializer functions twice.
とのことなので、removeProductFromCartは2回呼ばれます。
removeProductFromCartの1回目の実行では与えられたproductIdのProductを削除しますが、2回目の実行では
state.splice(removedItemIndex, 1)
の部分でstateが変更されている(与えられたproductIdのProductは削除されてしまっている)ので、
const removedItemIndex = state.findIndex((item) => item.id === productId)
の実行結果は-1になります。その後
state.splice(removedItemIndex, 1)
が実行されると、removedItemIndexは-1なので、stateは空になります。
const newState = [...state];
newState.splice(removedItemIndex, 1);
return newState;
と、stateを変更しなければ所望の動作になります。