[TIL] Recoil

jeongjwon·2023년 9월 15일
0

이론

목록 보기
5/19

Recoil

  • React에서 만든 상태 관리 라이브러리 ➡️ 사용 방법이 React의 useState와 매우 흡사
  • atoms (공유 상태)에서 selectors (순수 함수)를 거쳐 컴포넌트로 내려가는 data-flow graph 생성
    • atoms : 컴포넌트가 구독할 수 있는 상태의 단위
    • selectors: atoms 상태 값을 동기 또는 비동기 방식을 통해 변환
npm install recoil;

import { atom, selector } from "recoil";

Atoms

  • 상태의 단위로 업데이트되면 각각 구독된 컴포넌트는 새로운 값을 반영하여 다시 렌더링
  • 로컬 컴포넌트의 상태 대신 사용할 수 있다.
  • 여러 컴포넌트에서 사용되는 경우 모든 컴포넌트는 상태를 공유한다.
//CartItem.js
//장바구니에 담기는 아이템
export const CartAtom = atom({
  key: "CartAtom",
  default: [],
});
//Cart.tsx
import {useRecoilState, useRecoilValue} from "recoil";
function Cart(){
  //state
	const [cartItem, setCartItem] = useRecoilState(CartAtom);
  //value
  const carItem = useRecoilValue(CartAtom);
}
  • atom 을 통해 상태를 생성하고 key 와 default 가 필요하다. key 값은 전역적으로 고유성을 가져야 하고, 상태값은 default 를 통해 기본값을 가진다.
  • 상태를 사용할 컴포넌트에서는 useRecoilState hook 을 통해 상태값을 공유할 수 있다.
  • 전역 상태의 값만 가져올 수 있는 useRecoilValue hook 도 있다.

Selector

  • atoms 나 다른 selectors 를 입력으로 받아들이는 순수 함수 (pure function)으로 같은 인풋이 들어오면 같은 인풋을 리턴하고, side effect 가 존재하지 않는 함수이다. 상위의 atoms 나 selectors 가 업데이트되면 하위 selector 함수도 다시 실행된다.
  • 상태를 기반으로 하는 파생 데이터를 계산하는데 사용된다. 최소한의 상태 집합만 atoms 에 저장하고 다른 모든 파생되는 데이터는 selectors 에 명시한 함수를 통해 효율적으로 계산함으로써 쓸모없는 상태의 보존을 방지한다.
export const Selector = selector({
	key: '키값',
  	get: ({get}) => {
      const dnjsqhs = get(아톰)
      return 원본 변형값
    }
})
  • selector 함수를 사용해 정의한다.
  • get 속성은 계산될 함수이다.
    • 전달되는 get 인자를 통해 atoms와 다른 selectors에 접근할 수 있다.
    • 다른 atoms 나 selectors 에 접근하면 자동으로 종속 관계가 생성되므로, 참조했던 다른 atoms 나 selectors 가 업데이트되면 이 함수도 다시 실행된다.
//CartItem.js
//장바구니 아이템의 총 수량 selector
export const QuantitySelector = selecotr({
	key: 'QuantitySelector',
  	get: ({get}) => {
      const CurrentItem = get(CartItem);
      return CurrentItem.length.toLocaleString();
    },
});
//장바구니 아이템의 총 가격 selector
export const TotalPriceSelector = selector({
	key: 'TotalPriceSelector',
  	get: ({get}) => {
    	const CurrentItem = get(CartItem);
      	return CurrentItem.reduce(
          (acc, cur) => acc + cur.price, 0
         ).toLocaleString();
    },
});
//Cart.tsx
import {useRecoilState, useRecoilValue} from "recoil";
function Cart(){
  const TotalQuantity = useRecoilValue(QuantitySelector);
  const TotalPrice = useRecoilValue(TotalPriceSelector);
}
  • useRecoilValue() hook 을 사용하여 하나의 atom이나 selector 를 인자로 받아 대응하는 값을 반환한다.

참고 : https://www.youtube.com/watch?v=k5DLjVmMC2w

  • Main 페이지에서 각 물품을 추가 버튼을 클릭하면 CartItem 이라는 생성된 atom 객체에 값과 비교하여 없을 경우 추가한다.
 // 아톰 불러오기
  const [cartItem, setCartItem] = useRecoilState(CartAtom);

  // 이미 장바구니에 들어있는지 확인
  const isAlreadyInCart = cartItem.filter((e) => e.id === id).length;

  /**
   * 장바구니에 있는지 확인후, 없을때만 아톰에 추가
   */
  const AddToCart = () => {
    if (!isAlreadyInCart) {
      // spread operator 를 사용하여 원본 배열을 깊은 복사 후 추가
      setCartItem((prev) => [...prev, data]);
    }
  };

  • Cart 페이지에서 생성된 CartItem atom 을 useRecoilValue() 로 상태 값을 가져와 map 으로 나열시킬 수 있다.
  • selector 로 생성한 장바구니 아이템의 수량을 계산하는 QuantitySelector 와 총 가격을 계산하는 TotalPriceSelector 를 인자로 받아 대응하는 값으로 반환할 수 있다.
  • 삭제 버튼을 클릭하면 useSetRecoilState() hook 을 사용하여 상태값을 변경/삭제시킬 수 있다.
  const setCartItem = useSetRecoilState(CartAtom);

  const removeFromCart = () => {
    setCartItem((prev) => prev.filter((e) => e.id !== id));
  };



끝마치며

Redux 에서 Redux-Toolkit 로 변형해서 사용하면서 꽤 신선하다고 생각했는데, Recoil 에 대해 배우니 더 충격적이다. 너무나도 간단히 Atom 과 selector 를 생성해서 사용할 컴포넌트에서 상태값을 불러오고 공유시켜 반환시킬 수 있다니. 좀 더 익숙해질 수 있도록 해봐야겠다.

0개의 댓글