아래 내용은 코드로 배우는 React with 스프링부트 API서버 강의와 함께 설명이 부족한 부분에 대해서 조사하여 추가한 내용입니다.
export const getAllCartItems = async() => {
const res = await jwtAxios.get(`${prefix}/items`);
return res.data;
}
export const getCartItemsAsync = createAsyncThunk('getCartItemsAsync', () => {
return getAllCartItems();
});
const cartSlice = createSlice({
...
extraReducers: (builder) => {
builder
.addCase(getCartItemsAsync.fulfilled, (state, action) => {
console.log("getCartItemsAsync fulfilled action = ", action);
return action.payload;
})
...
}
})
export default cartSlice.reducer; // 반환은 reducer 로
export default configureStore({
reducer: {
"cartSlice" : cartSlice
}
})
function CartComponent() {
const {isLogin, loginState} = useLogin();
const dispatch = useDispatch();
useEffect(() => {
if(isLogin) {
dispatch(getCartItemsAsync());
}
}, [isLogin]);
...
}
function CartComponent() {
const cartItems = useSelector(state => state.cartSlice);
...
}
const useCart = () => {
const cartItems = useSelector(state => state.cartSlice);
const dispatch = useDispatch();
const getCart = () => {
dispatch(getCartItemsAsync());
}
const changeCart = (cartItem) => {
dispatch(postChangeCartItemAsync());
}
return {cartItems, getCart, changeCart}
}
export default useCart;
위의 5번과 6번은 useDispatch
를 이용하여 액션을 호출하고, useSelector
를 통해 상태를 가져와서 사용하고 있습니다. 이 두 가지 기능을 하나의 Hook 을 작성하여 보다 편리하게 사용하게 합니다.
따라서 위의 4, 5번에 해당하는 코드를 아래처럼 줄여서 사용할 수 있습니다.
function CartComponent() {
const { isLogin } = useLogin();
const {cartItems, getCart} = useCart();
useEffect(() => {
if (isLogin) {
getCart();
}
}, [isLogin])
}
function CartComponent() {
const { cartItems, getCart, changeCart } = useCart();
...
return (
<ul> {cartItems.map(item =>
<CartItemComponent {...item}
key={item.cartItemNo}
changeCart={changeCart}
email={loginState.email}/>)}
</ul>
)
...
}
+, - 버튼을 통해 수량 변경을 했을 때 수정 API 를 호출하기 위해 useCart 훅에 정의해놓은 changeCart 를 CartItemComponent 에 넘겨줍니다.
function CartItemComponent({ cartItemNo, itemName, price, itemId, quantity, imageFile, changeCart, email }) {
const handleClickQty = (amount) => {
changeCart({
cartItemNo: cartItemNo,
itemId: itemId,
quantity: quantity + amount,
email: email
})
}
return (
<li key={cartItemNo} className="border-2 w-full">
...
<button className="m-1 p-1 text-2xl bg-orange-500 w-8 rounded-lg"
onClick={() => handleClickQty(1)}>+ </button>
...
)
}
버튼 클릭 시, CartItem 객체를 만들고, 이를 전달받은 변경 함수의 파라미터로 하여 호출합니다. changeCart
는 dispatch
를 통해 Thunk 액션을 디스패치 하는 함수입니다.
해당 호출이 종료되면 extraReducer 에서 action.payload
를 반환하고, 반환된 값으로 상태가 변경되어 실시간으로 CartItemComponent 에서 보이는 수량도 변경되게 됩니다.
function ReadComponent({ itemId }) {
...
const {cartItems, changeCart} = useCart();
const {loginState} = useLogin();
const handleClickAdd = () => {
let quantity = 1;
const addedItem = cartItems.filter(item => item.itemId === parseInt(itemId))[0]; // 이미 추가된 아이템
if(addedItem) {
if(window.confirm("이미 추가된 상품입니다. 수량을 추가할까요?") === false) {
return;
}
quantity = addedItem.quantity + 1;
} else {
if(window.confirm("상품을 추가할까요?") === false) {
return;
}
}
changeCart({email: loginState.email, quantity: quantity, itemId: itemId});
}
return (...);
}
filter 를 통해 현재 보고있는 페이지의 상품이 장바구니에 있는지 판단합니다. 그 후 추가한다고 하면 수량을 증가시켜 호출합니다.
상품을 최초에 추가하는 경우에도 이미 서버에서 CartItemNo 가 없는 경우에 대한 처리를 했기 때문에 동일하게 changeCart
를 호출해도 상관없습니다.