리액트 & 타입스크립트 활용해서 장바구니 기능 구현했다.
웹/앱 개발에서 중요한 CRUD 기능 및 useReducer 활용해서 생산성이 좋게 설계해서 만듬.
기본적인 프로그래밍에 개념과 가장 기본적이 hooks 활용에 대해서 깊이 공부할 수 있다.
javascript filter, reduce, map, switch 활용했다.
useReducer 미리 숙지하고 하면 유용하게 사용할 수 있다.
- useReducer: useState처럼 state생성하고 관리하는 hook 이다.
- useReducer 잘사용하려면 3가지를 알아두면 좋다.
- Reducer, Dispatch, Action
- Reducer: State 상태를 업로드하는 역할
Dispatch: Reducer 요구를 하는 역할
Action: 어떤 요구를 보내는 역할 - Dispatch > Action > Reducer
- dispatch 객체 형태로 인자를 전달해서 recuder에서 예외처리를 해서 원하는
값을 파싱할 수 있다.
Github Source ☝ 구현 소스를 보실수 있습니다. (cart 폴더)
https://github.com/yoogukhyeon/react-camp
import React, { useState, useContext, useReducer, useEffect, ReactNode } from 'react';
import cartItems from './data';
import reducer from './reducer';
// ATTENTION!!!!!!!!!!
// I SWITCHED TO PERMANENT DOMAIN
const url = 'https://course-api.com/react-useReducer-cart-project';
interface IProps {
children: ReactNode;
}
interface Cart {
id: number;
img: string;
title: string;
price: number;
amount: number;
}
interface InitialState {
loading: boolean;
cart: Cart[];
total: number;
amount: number;
clearCart?: () => void;
remove?: ((key: number) => void) | any;
toggleAmount?: ((key: string | number) => void) | any;
}
//초기값 셋팅
const initialState: InitialState = {
loading: false,
cart: [],
total: 0,
amount: 0,
};
const AppContext = React.createContext(initialState);
const AppProvider = ({ children }: IProps) => {
//useReducer로 새로운 state 생성
//useState처럼 첫번째는 state 가진다.
//두번쨰 요소는 useReducer가 만들어준 dispatch가 들어있다.
//useReducer 인자를 2가지를 받는다. 두번째는 state 들어갈 초기값을 받는다.
const [state, dispatch] = useReducer(reducer, initialState);
const clearCart = () => {
dispatch({ type: 'CLEAR_CART' });
};
const remove = (id: number) => {
dispatch({ type: 'REMOVE', payload: id });
};
const toggleAmount = (id: number, type: string) => {
dispatch({ type: 'TOGGLE_AMOUNT', payload: { id, type } });
};
//state 변경될때마다 dispatch 실행
useEffect(() => {
dispatch({ type: 'GET_TOTAL' });
}, [state.cart]);
const fetchData = async () => {
dispatch({ type: 'LOADING' });
const response = await fetch(url);
const cart = await response.json();
dispatch({ type: 'DISPLAY_ITEMS', payload: cart });
};
useEffect(() => {
fetchData();
}, []);
return (
<AppContext.Provider
value={{
...state,
clearCart,
remove,
toggleAmount,
}}
>
{children}
</AppContext.Provider>
);
};
// 전역으로 쉽게 사용하기위해서 선언
export const useGlobalContext = () => {
return useContext(AppContext);
};
export { AppContext, AppProvider };
//reducer 함수는 2가지 인자를 받는다.
//여기서 예외처리를 해서 state 원하는 값을 만들수있다.
//이벤트가 일어날때 dispatch로 값을 전달해서 action활용하면 된다.
const reducer = (state: any, action: any) => {
switch (action.type) {
case 'CLEAR_CART':
// 초기화
return { ...state, cart: [] };
case 'REMOVE':
//filter 함수로 cart id화 playload id 맞지 않는것들만 새로운 배열로 반환 받는다.
return { ...state, cart: state.cart.filter((item: any) => item.id !== action.payload) };
case 'GET_TOTAL':
//reduce 함수를 활용해서 total 개수와 값을 구한다.
let { total, amount } = state.cart.reduce(
// 첫번째 인자는 누적값, 두번째 인자는 현잿값
(total: any, item: any) => {
const { price, amount } = item;
//누적값과 현재 값을 곱해서 총값을 구한다
const itemTotal = price * amount;
total.total += itemTotal; //total.total = total.total + itemTotal 같은뜻
total.amount += amount; // total.amount = total.amount + amount 같은뜻
return total;
},
//초기값 설정
{
total: 0,
amount: 0,
}
);
total = parseFloat(total.toFixed(2));
return { ...state, total, amount };
case 'LOADING':
return { ...state, loading: true };
case 'DISPLAY_ITEMS':
return { ...state, cart: action.payload, loading: false };
case 'TOGGLE_AMOUNT':
let tempCart = state.cart
.map((val: any) => {
if (val.id === action.payload.id) {
if (action.payload.type === 'inc') {
return { ...val, amount: val.amount + 1 };
} else {
return { ...val, amount: val.amount - 1 };
}
}
return val;
})
.filter((item: any) => item.amount !== 0);
return { ...state, cart: tempCart };
}
throw new Error('no action type');
};
export default reducer;
'React' 카테고리의 다른 글
React 중복 파일 업로드 오류 해결 방법 (0) | 2024.03.14 |
---|---|
immer를 사용해서 불변성을 지켜야하는 이유와 얕은 복사의 개념 (3) | 2024.01.05 |
React FAQ(자주 묻는 질문) 아코디언 기능 (35) | 2023.03.07 |
Next.js typescript mysql styled-components 셋팅 (30) | 2023.03.03 |
[React] 리다이렉트를 이용한 로그인 후 이전 페이지 이동 (26) | 2023.02.17 |