Reocil [Recoil]

SnowCat·2023년 2월 13일
0

React - Recoil

목록 보기
1/1
post-thumbnail

Motivation

  • 리액트 내장 state를 사용한 상태 관리에는 다음과 같은 문제가 있음
    • state를 여러 컴포넌트에서 공통되게 사용하는 경우 공통된 상위 요소까지 값을 끌어올려야 함
    • 이게 싫은 경우 Context api를 사용해야 하는데, 그 자체로는 단일 값만 저장 가능하며, consumer를 가지는 여러 값들의 집합을 담을 수 없음
  • 이로인해 트리의 최상단부터 말단까지의 코드 분할을 어렵게 함
  • Recoil은 리액트스러운 동작을 유지하면서 상태 관리를 쉽게 하고자 만들어짐

  • 리코일에서 상태 변화는 atom으로부터 selector를 거쳐 컴포넌트로 이동되는 구조를 가짐
  • 리코일은 다음과 같은 특성을 가짐
    • 공유 상태를 React의 내부 상태처럼 get/set 인터페이스로 접근 가능 (필요한 경우 reducer 등으로 캡슐화 가능)
    • 새로운 리액트 기능들과의 호환성을 가짐
    • 상태 정의는 분산되어 있음 -> 코드 분할 가능
    • 상태를 사용하는 컴포넌트를 수정하지 않더라도 상태를 파생된 데이터로 대체할 수 있으며, 동기식과 비동기식 사이를 전환할 수 있음
    • 애플리케이션이 변경되더라도 유지된 상태는 남아있게 할 수 있음

Atoms

  • atom은 상태의 단위이며, 업데이트와 구독이 가능함
  • atom이 업데이트되면 atom을 구독한 컴포넌트들은 새로운 값을 반영해 다시 렌더링 됨
  • 리액트의 로컬 컴포넌트 상태를 대신할 수 있으며, 런티임에서 생성될수도 있음
  • 동일한 atom을 여러 컴포넌트에서 사용하면, 모든 컴포넌트는 상태를 공유함
  • atom함수를 이용해 생성 가능
    리액트의 state와 비슷하게 기본값을 가지고, atom마다 고유한 key값을 설정해주어야 함
const fontSizeState = atom<number>({
  key: "fontSizeState",
  default: 16,
});
  • 컴포넌트에서 atom을 읽고 쓰려면 useRecoilState hook을 사용하며, 이는 React의 useState와 거의 비슷함
    state를 다른 컴포넌트와 공유하기 때문에 fontSize를 사용하는 컴포넌트 전부가 영향을 받게 됨
function FontButton() {
  const [fontSize, setFontSize] = useRecoilState(fontSizeState);
  return (
    <button onClick={() => setFontSize((size) => size + 1)} style={{fontSize}}>
      Click to Enlarge
    </button>
  );
}

// 버튼을 누를때마다 글자 크기가 1씩 커짐
function Text() {
  const [fontSize, setFontSize] = useRecoilState(fontSizeState);
  return <p style={{fontSize}}>This text will increase in size too.</p>;
}

Selectors

  • atom이나 다른 selectors를 입력으로 받아들이는 순수 함수
  • 상위 atom, selector가 업데이트되면 하위의 selector 함수도 다시 실행함
  • selector 역시 컴포넌트들이 atom처럼 구독 가능하면, selector가 업데이트되면 컴포넌트들도 다시 렌더링 됨
  • 꼭 필요한 상태만 atom에 계산하고 atom으로부터 파생되는 상태는 selector에 명시한 함수를 통해 계산함으로써 쓸모없는 상태의 보존 방지 가능
  • selector를 사용하면서 어떤 컴포넌트가 자신을 필요로 하고, 자신은 어떤 상태에 의존하는지를 추적할 수 있음
  • 컴포넌트 관점에서는 selector와 atom은 동일한 인터페이스를 가지기 때문에 서로 대체할 수 있음
const fontSizeLabelState = selector<React.CSSProperties>({
  key: 'fontSizeLabelState',
  // get은 fontSizeState을 받아 글자 크기를 출력으로 반환하는 순수함수
  get: ({get}) => {
    const fontSize = get(fontSizeState);
    const unit = 'px';

    return `${fontSize}${unit}`;
  },
});
  • Selector는 selector 함수를 사용해 정의함
  • key값과 함께 계산되어 반환될 함수값을 정의할 수 있는데, get 인자를 통해 다른 atom, selector에 접근 가능함
  • 참조했던 다른 atom, selector가 업데이트되면 이 함수 역시 다시 실행됨
  • 값을 사용할 때에는 값을 수정할 수 없기 때문에 useRecoilValue() 사용
function FontButton() {
  const [fontSize, setFontSize] = useRecoilState(fontSizeState);
  const fontSizeLabel = useRecoilValue(fontSizeLabelState);

  return (
    <>
      <div>Current font size: ${fontSizeLabel}</div>

      <button onClick={setFontSize(fontSize + 1)} style={{fontSize}}>
        Click to Enlarge
      </button>
    </>
  );
}

시작하기

  • React가 설치된 곳에서 Recoil 사용 가능

  • 다음 코드를 사용해 import
    ES5 미만 코드에서는 호환성 문제 발생

npm install recoil
  • CDN을 통해 사용할 경우 다음 스크립트 추가
<script src="https://cdn.jsdelivr.net/npm/recoil@0.0.11/umd/recoil.production.js"></script>
  • ESLint 설정에 additionalHooks 추가
{
  "plugins": ["react-hooks"],
  "rules": {
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": [
      "warn",
      // 이부분 추가
      // 전달된 종석성이 잘못됬을 때 경고 표시
      {
        "additionalHooks": "useRecoilCallback"
      }
    ]
  }
}
  • Root component에 RecoilRoot 추가
import React from 'react';
import {
  RecoilRoot,
  atom,
  selector,
  useRecoilState,
  useRecoilValue,
} from 'recoil';

function App() {
  return (
    <RecoilRoot>
      <CharacterCounter />
    </RecoilRoot>
  );
}
profile
냐아아아아아아아아앙

0개의 댓글