Project/Study

[React] Recoil

by somida 2021. 7. 29.

Recoil

React를 위한 상태 관리 라이브러리

 

설치

# npm
npm install recoil

# yarn
yarn add recoil

 

ESLint

eslint-plugin-react-hooks를 사용하는 경우 useRecoilCallback을 additionalHooks에 추가하는 것을 권장

useRecoilCallback()을 사용하기 위해 전달된 종속성이 잘못 지정되었을 때 경고와 해결 방안 제시해줌

{
  "plugins": ["react-hooks"],
  "rules": {
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": [
      "warn",
      {	// 추가
        "additionalHooks": "useRecoilCallback"
      }
    ]
  }
}

 

사용

  • RecoilRoot : recoil을 사용하기 위해 Root Component에 추가
import { RecoilRoot } from 'recoil';

function App() {
  return (
    <RecoilRoot>
      <Main />
    </RecoilRoot>
  );
}

 

주요 개념

atoms(공유 상태)에서 selectors(순수 함수)를 거쳐 React Component로 내려가는 data-flow graph를 만들 수 있음

  • Atoms : 컴포넌트가 구독할 수 있는 상태의 단위
  • Selectors : atoms 상태 값을 동기 또는 비동기 방식을 통해 변환

 

Atoms

  • 상태의 단위이며 업데이트와 구독이 가능
  • atom이 업데이트되면 각각의 구독된 Component는 새로운 값을 반영해 다시 랜더링 됨
  • 동일한 Atom이 여러 Component에서 사용되는 경우 모든 Component가 상태를 공유

생성

  • key : 전역적으로 고유하도록 설정
  • default : React Component 상태처럼 기본값 설정
const fontSizeState = atom({
	key: 'fontSizeState',
    default: 15,
});

 

사용

  • useRecoilState : useState와 비슷하지만, 상태가 Component 간에 공유될 수 있다는 차이가 있음
function FontButton() {
  const [fontSize, setFontSize] = useRecoilState(fontSizeState);
  return (
    <button onClick={() => setFontSize((size) => size + 1)} style={{fontSize}}>
      Click to Enlarge
    </button>
  );
}
  • useRecoilValue : atom을 읽기만 할 때 사용. atom이 Component를 등록하도록 함
  • useSetRecoilState : atom에 쓰려고만 할 때 사용
  • useResetRecoilState : atom을 초깃값으로 초기화할 때 사용
  • useRecoilCallback : Component가 등록되지 않고 atom값을 읽어야 하는 경우

 

Selectors

  • atoms나 다른 selectors를 입력으로 받아들이는 순수 함수
  • 상위 atoms나 selectors가 업데이트되면 하위의 selector도 다시 실행됨
  • Component들은 selectors를 atoms처럼 구독할 수 있고, selectors가 변경되면 Component들도 다시 랜더링 됨
  • 최소한의 상태 집합만 atoms에 저장하고, 다른 모든 파생 데이터는 selectors에 명시한 함수를 통해서 쓸모없는 상태들의 보존을 방지

 

생성

  • get : 계산될 함수
    • get (인자) : atoms, 다른 selectors에 접근 가능
    • get을 통해서 참조되는 다른 atoms나 selectors가 업데이트되면 이 함수도 다시 실행됨
const fontSizeLabelState = selector({
  key: 'fontSizeLabelState',
  get: ({get}) => {
    const fontSize = get(fontSizeState);
    const unit = 'px';

    return `${fontSize}${unit}`;
  },
});

 

  • set : 쓰기 가능한 selector

 

사용

  • useRecoilValue : 하나의 atom이나 selector를 인자로 받아 대응하는 값을 반환
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>
    </>
  );
}

 

비동기 selector

  • Selector는 비동기 함수를 사용할 수 있으며 Promise를 출력 값으로 반환 가능
const myQuery = selector({
  key: 'MyQuery',
  get: async ({get}) => {
    return await myAsyncQuery(get(queryParamState));
  },
});
  • 비동기 Selector를 사용할 때는 Suspense, fallback 사용
function ResultsSection() {
  return (
    <React.Suspense fallback={<div>Loading...</div>}>
      <ExampleApp />
    </React.Suspense>
  );
}

 

class Loadable

atom, selector의 최신 상태를 대표

사용 가능(hasValue)하거나 에러상태(hasError) 거나 로딩 상태(loading)이거나 셋 중 하나의 상태를 가짐

function UserInfo({userID}) {
  const userNameLoadable = useRecoilValueLoadable(userNameQuery(userID));
  switch (userNameLoadable.state) {
    case 'hasValue':
      return <div>{userNameLoadable.contents}</div>;
    case 'loading':
      return <div>Loading...</div>;
    case 'hasError':
      throw userNameLoadable.contents;
  }
}
  • useRecoilStateLoadable : 비동기 selector 값을 읽어올 때 사용
  • useRecoilValueLoadable : 비동기 selector 값을 읽을 때 사용

 


Reference

반응형

'Project > Study' 카테고리의 다른 글

[Spring Boot] 빌드 도구  (0) 2021.08.03
[React] React-Query  (0) 2021.08.02
[WebSocket] Spring Boot + React WebSocket 코드 구현  (0) 2021.07.26
Spring Boot 유효성 검사  (0) 2021.07.20
Spring Session + Redis  (0) 2021.07.19

댓글