DEV Community

Dylan
Dylan

Posted on

2 3

Redux에 custom hook으로 Computed value 달아주기

React를 쓰는 많은 분들이 프로젝트를 시작하기 전 첫 번째로 고민하게 되는 기술스택이 글로벌 상태관리 라이브러리 reduxmobx 중 어떤 것을 선택할까가 아닐까 합니다.
깃헙 스타수로 보면 리덕스의 인기가 좀 더 많은 것을 알 수 있습니다. 저도 리덕스를 주로 쓰지만 mobx 의 장점 중 하나는 데코레이터 문법과 computed value 에 있다고 생각합니다.

@computed get discountedPrice() {
  return this.price * this.discount
}

이런식으로 store에 데코레이터 문법으로 getter를 달아주는 방식입니다. redux에는 api가 없지만 reselect 라는 라이브러리를 통해서 비슷한 방식을 구현할 수 있습니다.
이제 react에 훅이 도입되면서 추가 라이브러리 없이도 computed value를 쉽게 구현할 수 있게 되었습니다.

스토어 fruitStore에 서버로부터 과일의 할인율과 가격 데이터를 받아 저장되어 있다고 해보겠습니다.

const initialState: FruitStore = {
  apple: { discount: 0.03, price: 1000 },
  orange: { discount: 0.12, price: 3000 },
  grape: { discount: 0.2, price: 8000 },
  ...
};

저장된 데이터는 Tag 컴포넌트에 3가지 방법으로 표현됩니다.

  1. 할인된 가격을 표현해줍니다.
  2. 할인이 적용된 최종 결제가격을 보여줍니다.
  3. 할인율을 읽기 쉽게 %로 보여줍니다.

데이터를 view에 표현하기 위해서는 항상 아래와 같이 값을 변환해주어야 합니다.

const discountedPrice: number = price * discount;
const billingPrice: number = price * (1 - discount);
const discountPercent: string = `${discount * 100} %`;

만약에 위의 데이터를 쓰는 컴포넌트가 여러개라면 각 컴포넌트마다 같은 코드를 복사/붙여넣기 해야 할 것이고 유지보수하기도 점점 어려워질 것입니다.
스토어에서 3가지 데이터를 모두 저장하는 것도 생각해볼 수 있습니다. 하지만 이런 경우에 nested 된 형태로 스토어를 관리해야 될 가능성이 높아지고, view의 형태가 다양해지면 복잡성이 기하급수적으로 커지게 됩니다.

커스텀훅과 useMemo를 조합해 computed value를 구현해보겠습니다.

// custom hook
function useFruitTag(fruit: string) {
  const { discount, price } = useSelect(({ fruitStore }) => fruitStore[fruit]);

  const discountedPrice = useMemo(() => price * discount, [discount, price]);
  const billingPrice = useMemo(() => price * (1 - discount), [discount, price]);
  const discountPercent = useMemo(() => `${discount * 100} %`, [discount]);

  return {
    discountedPrice,
    billingPrice,
    discountPercent,
  };
}

// component
function AppleTag() {
  const { discountedPrice, billingPrice, discountPercent } = useFruitTag('apple');

  return (
    <div>
      <h1>Apple</h1>
      <p>{discountedPrice}</p>
      <p>{billingPrice}</p>
      <p>{discountPercent}</p>
    </div>
  );
}

이런 방식으로 reselect@computed 없이도 computed value를 쉽게 만들어 쓸 수 있습니다. useMemo를 이용해서 캐싱된 값을 쓰기도 쉽고, 과일의 종류가 늘어나거나 대응해야 하는 view가 늘어나더라도 쉽게 처리할 수 있습니다. 무엇보다 관심사별로 로직이 분리되면서 코드를 읽기 쉬워진다는 게 가장 큰 장점인 것 같습니다.

p.s
useMemoreselect를 완전히 대체한다는 뜻은 아닙니다. 관련된 내용은 여기 에 잘 설명되어 있습니다.

SurveyJS custom survey software

Simplify data collection in your JS app with a fully integrated form management platform. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more. Integrates with any backend system, giving you full control over your data and no user limits.

Learn more

Top comments (0)

SurveyJS custom survey software

JavaScript Form Builder UI Component

Generate dynamic JSON-driven forms directly in your JavaScript app (Angular, React, Vue.js, jQuery) with a fully customizable drag-and-drop form builder. Easily integrate with any backend system and retain full ownership over your data, with no user or form submission limits.

Learn more