StudyForYou / ouahhan-typescript-with-react

우아한 타입스크립트 with 리액트 스터디 레포 🧵
4 stars 0 forks source link

#30 [10장_1] 다음 쇼핑카트 컴포넌트에 타입스크립트를 적용하고 상태 관리로 useReducer를 사용하도록 바꿔주세요! #48

Open drizzle96 opened 3 months ago

drizzle96 commented 3 months ago

❓문제

다음 쇼핑카트 컴포넌트에 타입스크립트를 적용하고 상태 관리로 useReducer를 사용하도록 바꿔주세요!

import { useState } from 'react'

const ShoppingCart = () => {
  const [cart, setCart] = useState({ items: [], total: 0 })

  const addItem = (item) => {
    setCart((prevCart) => {
      const newTotal = prevCart.total + item.price
      return {
        ...prevCart,
        items: [...prevCart.items, item],
        total: newTotal,
      }
    })
  }

  const removeItem = (index) => {
    setCart((prevCart) => {
      const itemPrice = prevCart.items[index].price
      const newItems = prevCart.items.filter((_, i) => i !== index)
      const newTotal = prevCart.total - itemPrice
      return {
        ...prevCart,
        items: newItems,
        total: newTotal,
      }
    })
  }

  return (
    <div>
      <h1>Shopping Cart</h1>
      <ul>
        {cart.items.map((item, index) => (
          <li key={index}>
            {item.name} - ${item.price}
            <button onClick={() => removeItem(index)}>Remove</button>
          </li>
        ))}
      </ul>
      <div>Total: ${cart.total}</div>
      <button onClick={() => addItem({ name: 'Apple', price: 1 })}>Add Apple</button>
    </div>
  )
}

export default ShoppingCart

🎯답변

qooktree1 commented 2 months ago

아는 형님 답변...

export interface CartState { items: CartItem []; total: number; }

export type CartAction = { type: 'ADD_ITEM'; payload: CartItem } | { type: 'REMOVE_ITEM'; payload: number };


```ts
// reducer.ts
import { CartState, CartAction } from './types';

export const cartReducer = (state: CartState, action: CartAction): CartState => {
  switch (action.type) {
    case 'ADD_ITEM':
      return {
        ...state,
        items: [...state.items, action.payload],
        total: state.total + action.payload.price,
      };
    case 'REMOVE_ITEM':
      const itemPrice = state.items[action.payload].price;
      return {
        ...state,
        items: state.items.filter((_, index) => index !== action.payload),
        total: state.total - itemPrice,
      };
    default:
      return state;
  }
};
// ShoppingCart.tsx
import React, { useReducer } from 'react';
import { CartState, Item } from './types';
import { cartReducer } from './reducer';

const initialState: CartState = {
  items: [],
  total: 0,
};

const ShoppingCart: React.FC = () => {
  const [state, dispatch] = useReducer(cartReducer, initialState);

  const addItem = (item: Item) => {
    dispatch({ type: 'ADD_ITEM', payload: item });
  };

  const removeItem = (index: number) => {
    dispatch({ type: 'REMOVE_ITEM', payload: index });
  };

  return (
    <div>
      <h1>Shopping Cart</h1>
      <ul>
        {state.items.map((item, index) => (
          <li key={index}>
            {item.name} - ${item.price}
            <button onClick={() => removeItem(index)}>Remove</button>
          </li>
        ))}
      </ul>
      <div>Total: ${state.total}</div>
      <button onClick={() => addItem({ name: 'Apple', price: 1 })}>Add Apple</button>
    </div>
  );
};

export default ShoppingCart;
drizzle96 commented 2 months ago
import { Reducer, useReducer } from 'react'

type Item = {
  name: string
  price: number
}

type State = {
  items: Item[]
  total: number
}

type Action =
  | { type: 'ADD_ITEM', item: Item }
  | { type: 'REMOVE_ITEM', index: number }

const reducer: Reducer<State, Action> = (state, action) => {
  switch (action.type) {
    case 'ADD_ITEM':
      return {
        ...state,
        items: [...state.items, action.item],
        total: state.total + action.item.price,
      }
    case 'REMOVE_ITEM':
      return {
        ...state,
        items: state.items.filter((_, i) => i !== action.index),
        total: state.total - state.items[action.index].price,
      }
  }
}

const initialState: State = {
  items: [],
  total: 0,
}

const ShoppingCart = () => {
  const [cart, dispatch] = useReducer(reducer, initialState)

  return (
    <div>
      <h1>Shopping Cart</h1>
      <ul>
        {cart.items.map((item, index) => (
          <li key={index}>
            {item.name} - ${item.price}
            <button onClick={() => dispatch({ type: 'REMOVE_ITEM', index })}>Remove</button>
          </li>
        ))}
      </ul>
      <div>Total: ${cart.total}</div>
      <button onClick={() => dispatch({ type: 'ADD_ITEM', item: { name: 'apple', price: 10000 } })}>Add Apple</button>
    </div>
  )
}

export default ShoppingCart