0

I am creating a cart page for an ecommerce site.

I am using redux for state management and react-query for data management. I am new in redux and react-query. I am facing some problem while updating localStorage.

Redux initially setting cartItem from localStorage. But the problem occurs when I try to remove a item from the cart. When I click on remove button react-query trigger isLoading. I try to use isFetching but it did the same. How can I fix that?

Cart Page

const CartPage = () => {
  const { cartItems } = useSelector((state) => state.cart);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    if (cartItems.length > 0) {
      setLoading(false);
    }
  }, [cartItems]);

  const ids = cartItems.map((item) => item.id);

  const {
    data: cartData,
    isLoading,
    isError,
    error,
    status,
    isFetching
  } = useQuery(["cart", ids], () => getCartItems(ids), {
    staleTime: Infinity
  });


  if (loading || isLoading) {
    console.log("Fetching data...");
    return <p>Loading...</p>;
  }


  return (
      <div className="container">
        {
          !cartItems.length
            ? <div className="cart-empty">
              <h2 className="cart-empty__title">Your cart is empty</h2>
              <Link href="/shop" className="btn btn-primary">Go To Shop</Link>
            </div>
            : <div>
                <div className="cart-details-main-block" id="dynamic-cart">
                {Children.toArray(
                  cartData.map((item) => {
                    return <CartProduct {...item} />;
                  })
                )}
              </div>
              </div>
            </div>
          }
      </div>
    </div>
  );
};

CartProduct

export default function CartProduct({ _id, title, price, image }) {

  const dispatch = useDispatch();
  // remove item from cart and update the cart ui 

  const handleRemoveFromCart = (id) => {
    dispatch(removeItemFromCart({ id }));
  }

  return (
    <div className="cart-product cart-product--main">
      {/* .product-quantity-block starts */}
      <div className="total-block pl-7 pl-xs-0">
        <div className="total-block-inner">
          <h3 className="price-text">
            $140
          </h3>
          <button onClick={() => handleRemoveFromCart(_id)}>
            &#10006;
          </button>
        </div>
      </div>
    </div>
  )
}

cartSlice

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

const CART_KEY = 'cart';

// Load cart data from local storage
const loadCartFromStorage = () => {
  if (typeof window !== 'undefined') {
    const cartData = localStorage.getItem(CART_KEY);
    if (cartData) {
      return JSON.parse(cartData);
    }
  }
  return { cartItems: [] };
};

// Save cart data to local storage
const saveCartToStorage = (cartData) => {

  if (typeof window !== 'undefined') {

    localStorage.setItem(CART_KEY, JSON.stringify(cartData));
  }
};

// Define the cart slice
const cartSlice = createSlice({
  name: 'cart',
  initialState: loadCartFromStorage(),
  reducers: {
    addItemToCart: (state, action) => {
      // Check if the item already exists in the cart

      const itemIndex = state.cartItems.findIndex((item) => item.id === action.payload.id);

      if (itemIndex >= 0) {
        // If the item already exists, update the quantity
        state.cartItems[itemIndex].quantity += action.payload.quantity;

      } else {
        // If the item doesn't exist, add it to the cart
        state.cartItems.push(action.payload);

      }
      // Save the cart data to local storage
      saveCartToStorage(state);
    },
    removeItemFromCart: (state, action) => {

      // Remove the item from the cart
      state.cartItems = state.cartItems.filter((item) => item.id !== action.payload.id);

      // Save the cart data to local storage
      saveCartToStorage(state);
    },
  },
});

// Export the actions
export const { addItemToCart, removeItemFromCart } = cartSlice.actions;

// Export the reducer
export default cartSlice.reducer;
Wahlstrommm
  • 684
  • 2
  • 7
  • 21

1 Answers1

0

When I click on remove button react-query trigger isLoading

From a useQuery perspective, you have all ids in the QueryKey, because they are used in the QueryFunction. That is correct, but it also means that every time the ids change, you get a new cache entry. This means you'll get a loading state again because for that cache entry, there is no data yet.

There are multiple ways to solve that issue, the easiest being to add keepPreviousData: true:

useQuery(["cart", ids], () => getCartItems(ids), {
  staleTime: Infinity,
  keepPreviousData: true
});

this will make sure data is still displayed as-is while you're transitioning between QueryKeys.

TkDodo
  • 20,449
  • 3
  • 50
  • 65