mobX 사용하기

jeong dain·2022년 9월 22일
0

React + TypeScript + mobX 를 이용한 장바구니 기능 구현

React + TypeScript 프로젝트 생성

npx create-react-app supermarket --template typescript

mobX 설치

npm i mobx-react

src/stores/BasketStore.ts

import { makeObservable, action, observable, computed, toJS } from "mobx";
import { Product } from "./ProductStore";

class BasketClass {
  
  /**
  * itemList 를 담아주는 상태값
  */
  itemList: Product[] = [];

  /**
  *	장바구니에 담긴 아이템 가격의 합계를 담아줄 상태값
  */
  totalPrice: number = 0;

  constructor() {
    makeObservable(this, {
      itemList: observable,
      totalPrice: observable,
      updateItem: action,
      returnItem: action,
      removeItem: action,
      setTotalPrice: action,
      getItems: computed,
      getTotalPrice: computed,
    });
  }

  /**
   *  동일한 아이템 선택 시 수량 추가,
   *  동일하지 않을 경우 아이템 추가
   */
  updateItem(item: Product) {
    const found = this.getItems.findIndex((el) => el.id === item.id);

    if (found >= 0) this.itemList[found].choice++;
    else this.itemList = [...this.itemList, item];

    this.setTotalPrice();
  }

  /**
   * 아이템 수량 1개씩 빼기
   */
  returnItem(id: number) {
    this.itemList = this.itemList.map((item) => {
      if (item.id === id && item.choice > 0) item.choice--;
      return item;
    });
    this.setTotalPrice();
  }

  /**
   * 리스트에서 아이템 삭제
   */
  removeItem(id: number) {
    const idx = this.itemList.findIndex((el) => el.id === id);
    this.itemList.splice(idx, 1);
    this.setTotalPrice();
  }

  /**
   * 전체 가격 구하기
   */
  setTotalPrice() {
    this.totalPrice = this.itemList.reduce((acc: number, current: Product) => {
      return acc + current.price * current.choice;
    }, 0);
  }

  get getItems() {
    return toJS(this.itemList);
  }

  get getTotalPrice() {
    return toJS(this.totalPrice);
  }
}

const basketClass = new BasketClass();
export default basketClass;

src/stores/ProductStore.ts

import { makeObservable, action, observable, computed, toJS } from "mobx";

export interface Product {
  id: number;
  name: string;
  price: number;
  choice: number;
}

class ProductClass {
  productList: Product[] = [
    { id: 0, name: "초코우유", price: 1800, choice: 1 },
    { id: 1, name: "당근", price: 2000, choice: 1 },
    { id: 2, name: "시리얼", price: 8500, choice: 1 },
  ];
  
  constructor() {
    makeObservable(this, {
      productList: observable,
      removeProduct: action,
    });
  }
  
  removeProduct(id: number) {
    this.productList.splice(id, 1);
  }
}

const productClass = new ProductClass();
export default productClass;

src/components/Market.tsx

import "../css/Market.css";
import productStore from "../stores/ProductStore";
import basketStore from "../stores/BasketStore";
import { observer } from "mobx-react";

function Market() {
  return (
    <div className="Market">
      <div className="Market-wrapper">
        <div className="products-wrapper">
          <h1>판매상품</h1>
          <ul>
            {productStore.productList.map((product) => (
              <li
                key={product.id}
                onClick={() => basketStore.updateItem(product)}
              >
                <div className="item-name">{product.name}</div>
                <div className="item-price">{product.price}</div>
                <button onClick={() => productStore.removeProduct(product.id)}>
                  X
                </button>
              </li>
            ))}
          </ul>
        </div>
        <div className="basket-wrapper">
          <h1>장바구니</h1>
          <ul>
            {basketStore.itemList.map((item) => (
              <li key={item.id}>
                <div className="item-name">{item.name}</div>
                <div className="item-price">{item.price}</div>
                <div className="item-choice">{item.choice}</div>
                <button onClick={() => basketStore.returnItem(item.id)}>
                  -
                </button>
                <button onClick={() => basketStore.removeItem(item.id)}>
                  X
                </button>
              </li>
            ))}
          </ul>
          <div className="total">total : {basketStore.totalPrice}</div>
        </div>
      </div>
    </div>
  );
};

// observer 로 감싸주어야 상태값이 변경될 때 리렌더링 된다
export default observer(Market);
profile
Web Frontend Developer #TypeScript #React #NextJS🤸‍♀️

0개의 댓글