Search code examples
javascriptreactjsreduxreact-reduxredux-toolkit

why my if/else block doesn't work in redux toolkit reducer?


So basically I am making a shopping cart and I want to add a functionality if an item is already in the cart then increase it's quantity by 1. If you add same item and they have different sizes then show them separetely. I managed to deal with increasing the quantity in my reducer's logic but when I add another block condition it doesn't work. Here is the code:

import { createSlice } from "@reduxjs/toolkit";

const initialState = {
  bagData: [],
};

export const bagDataSlice = createSlice({
  name: "bagDataProducts",
  initialState,
  reducers: {
    sendDataToCardComponent: (state, action) => {
      let { id, size } = action.payload;

      const findItemById = state.bagData.find(
        (item) => item.id === id && item.size === size
      );

      if (findItemById) {
        state.bagData.filter((item, i) => (state.bagData[i].quantity += 1));
      } else {
        state.bagData.push({ ...action.payload, quantity: 1 });
      }
    },
    increaseItemQuantity: (state, { payload }) => {
      state.bagData.filter((item, i) =>
        item.id === payload ? (state.bagData[i].quantity += 1) : item
      );
    },
    decreaseItemQuantity: (state, { payload }) => {
      state.bagData.filter((item, i) =>
        item.id === payload && item.quantity > 1
          ? (state.bagData[i].quantity -= 1)
          : item
      );
    },
    removeItem: (state, { payload }) => {
      state.bagData = state.bagData.filter((item) => item.id !== payload);
    },
  },
});

when I add the condition of

 const findItemById = state.bagData.find(
        (item) => item.id === id && item.size === size
      );

it only increments the item without checking it's size, only checks it's id even though there are 2 conditions for that function. Could you please explain that to me?


Solution

  • state.bagData.filter((item, i) => (state.bagData[i].quantity += 1));
    

    For your first case, this is updating every item's quantity if you found a matching item by id and size. Since you've already found the item and stored it in findItemById, you should be able to use the following.

    Caveat, Immer only supports mutating array elements by index so use findIndex() instead of find().

    const itemIndex = state.bagData.findIndex(
      (item) => item.id === id && item.size === size
    );
    
    if (itemIndex !== -1) {
      state.bagData[itemIndex].quantity++;
    } else {
      state.bagData.push({ ...action.payload, quantity: 1 });
    }
    

    Here's a quick demo showing that this works

    const initialState = {
      bagData: [{
        id: 1,
        quantity: 1,
        size: "S"
      }]
    };
    
    const sendDataToCardComponent = (action) =>
      immer.produce(initialState, (state) => {
        let { id, size } = action.payload;
    
        const itemIndex = state.bagData.findIndex(
          (item) => item.id === id && item.size === size
        );
    
        if (itemIndex !== -1) {
          state.bagData[itemIndex].quantity++;
        } else {
          state.bagData.push({ ...action.payload, quantity: 1 });
        }
      });
    
    console.log(
      "increment existing",
      sendDataToCardComponent({ payload: { id: 1, size: "S" } })
    );
    console.log(
      "add new",
      sendDataToCardComponent({ payload: { id: 1, size: "M" } })
    );
    .as-console-wrapper { max-height: 100% !important; }
    <script src="https://cdn.jsdelivr.net/npm/immer"></script>


    As mentioned in the comments, you're misusing Array.prototype.filter() which should only be used to return a new array with items filtered in or out based on a predicate. Your code can be cleaned up somewhat

    increaseItemQuantity: (state, { payload }) => {
      const found = state.bagData.findIndex(({ id }) => id === payload);
      if (found !== -1) {
        state.bagData[found].quantity++;
      }
    },
    decreaseItemQuantity: (state, { payload }) => {
      const found = state.bagData.findIndex(({ id }) => id === payload);
      if (found !== -1) {
        state.bagData[found].quantity--;
      }
    },
    

    Your last reducer is using filter() correctly but Immer also supports splice()

    removeItem: (state, { payload }) => {
      const found = state.bagData.findIndex(({ id }) => id === payload);
      if (found !== -1) {
        state.bagData.splice(found, 1);
      }
    },