Recoil (1)

BirdsOnTree·2022년 10월 18일
0

React

목록 보기
8/10
post-thumbnail

전 글에서는 MobX에 대해서 알아보았습니다. 이번에는 다른 상태 관리 라이브러리인 Recoil에 대해서 알아볼려고 합니다. 작은 프로젝트를 진행하며 Redux와의 차이점을 비교해보면서 하면서 알아볼려고 합니다.

설치

npm install recoil

yarn add recoil

RecoilRoot

recoil 상태를 사용하기 위해서는 컴포넌트를 RecoilRoot로 감싸줘야 한다. root에서 감싸주었다.

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <RecoilRoot>
    <App />
  </RecoilRoot>
);

Atom

Atom은 상태(state)의 일부를 나타낸다.
Atoms는 어떤 컴포넌트에서나 읽고 쓸 수 있고, atom의 값을 읽기 위해서는 구독을 해야한다. 그 후 atom에 어떠한 변화가 있다면 해당 atom을 구독한 모든 컴포넌트들이 재 렌더링이 될것이다.

import { atom } from "recoil";

export default firstStair = atom({
  key: "firstStair",
  default: [],
});

useRecoilState

firstStair을 읽기 위해서는 useRecoilState을 사용하면 된다. 사용법은 useState와 동일하다.
useRecoilValue 을 사용해서 FirstStair을 읽을수도 있다.

import { useRecoilState } from "recoil";

function Game() {
  const [firstStair, setFirstStair] = useRecoilState(FirstStair);
  
  const firstStair = useRecoilValue(FirstStair)
  
  return <div>1</div>;
}

Selector

추가공부필요

atom 을 원하는 대로 변형해 값을 리턴받는다.

Selector는 Recoil에서 함수나 파생된 상태를 나타낸다.
주어진 종속성 값 집합에 대해 항상 동일한 값을 반환하는 부작용이 없는 "순수함수"라고 생각하면 된다.
get 함수만 제공되면 Selector는 읽기만 가능한 RecoilValueReadOnly 객체를 반환한다. set 함수 또한 제공되면 Selector는 쓰기 가능한 RecoilState 객체를 반환한다.

selector 은 atom 을 활용해 개발자가 원하는 대로 값을 뽑아서 사용할 수 있는 API 이다. 하지만 역시 개념이 조금 이해가 안갔다.

function selector<T>({
  key: string,
  get: ({
    get: GetRecoilValue
  }) => T | Promise<T> | RecoilValue<T>,

  set?: (
    {
      get: GetRecoilValue,
      set: SetRecoilState,
      reset: ResetRecoilState,
    },
    newValue: T | DefaultValue,
  ) => void,

  dangerouslyAllowMutability?: boolean,
})
type ValueOrUpdater<T> =
  | T
  | DefaultValue
  | ((prevValue: T) => T | DefaultValue);
type GetRecoilValue = <T>(RecoilValue<T>) => T;
type SetRecoilState = <T>(RecoilState<T>, ValueOrUpdater<T>) => void;
type ResetRecoilState = <T>(RecoilState<T>) => void;

key - 내부적으로 atom을 식별하는데 사용되는 고유한 문자열이다. 어플 전체에 다른 atom과 selector에서 key값은 고유해야 한다.

get - 파생된 상태의 값을 평가하는 함수이다. 값을 직접 반환하거나 비동기적인 Promise나 또는 같은 유형을 나타내는 다른 atom이나 selector를 반환할 수 있다. 첫 번째 매개변수로 다음 속성을 포함하는 객체를 전달한다

  • get - 다른 atom이나 selector로부터 값을 찾는데 사용되는 함수이다. 이 함수에 전달된 모든 atom과 selector는 암시적으로 selector에 대한 의존성 목록에 추가된다. Selector의 의존성이 변경되면 Selector가 다시 평가된다.

set? - 이 속성이 설정되면 selector는 쓰기 가능한 상태를 반환한다. 첫번째 매개변수로 콜백 객체와 새로 입력 값이 전달된다. 사용자가 selector를 재설정할 경우 새로 입력 값은 T 타입의 값 또는 DefaultValue 타입의 객체일 수 있다. 콜백에는 다음이 포함된다.:

  • get - 다른 atom이나 selector로부터 값을 찾는데 사용되는 함수. 이 함수는 selector를 주어진 atom이나 selector를 구독하지 않는다.
  • set - 업스트림 Recoil 상태의 값을 설정할 때 사용되는 함수. 첫 번째 매개변수는 Recoil 상태, 두 번째 매개변수는 새로운 값이다. 새로운 값은 업데이트 함수나 재설정 액션을 전파하는 DefalutValue 객체일 수 있다.

dangerouslyAllowMutability - Selector는 파생된 상태의 "순수 함수"를 나타내며 항상 동일한 의존성 입력 값 집합에 대하여 동일한 값을 반환해야 한다. 이를 보호하기 위해 selector에 저장된 모든 값은 기본적으로 고정되어 있다. 경우에 따라 이 옵션을 사용하여 재정의해야 할 수 있다.


동적 의존성

읽기만 가능한 selector는 의존성을 기준으로 selector의 값을 평가하는 get 메서드를 갖는다. 의존성 중 어떠한 것이 업데이트 되면 selector는 다시 평가된다. 이전 의존성의 값에 따라 다른 추가적인 의존성을 동적으로 사용할 수 있다.

const toggleState = atom({key: 'Toggle', default: false});

const mySelector = selector({
  key: 'MySelector',
  get: ({get}) => {
    const toggle = get(toggleState);
    if (toggle) {
      return get(selectorA);
    } else {
      return get(selectorB);
    }
  },
});
// mySelector는 toggleState atom과 toggleState의 selectorA 아님 seletorB에도 의존

쓰기 가능한 Selector

양방향 selector는 입력 값을 매개변수로 받고 데이터 흐름 그래프를 따라 업스트림에서 변경사항을 전파하는 데 사용할 수 있다. 사용자가 selector를 새 값으로 설정하거나 selector를 재설정할 수 있기 때문에 입력 값은 selector가 나타내는 타입과 동일하거나 재설정 작업을 나타내는 DefaultValue 객체 중 하나이다.

이 간단한 selector는 기본적으로 atom을 감싸서 필드를 추가한다. 이것은 단지 설정과 재설정 작업을 업스트림 atom까지 통과한다.

const transformSelector = selector({
  key: 'TransformSelector',
  get: ({get}) => get(myAtom) * 100,
  set: ({set}, newValue) =>
    set(myAtom, newValue instanceof DefaultValue ? newValue : newValue / 100),
});

비동기 Selector

Selector는 또한 비동기 평가 함수를 가지고 있으며 Promise를 출력값으로 반환할 수 있다.

const myQuery = selector({
  key: 'MyQuery',
  get: async ({get}) => {
    return await myAsyncQuery(get(queryParamState));
  },
});

비동기 예시

const myQuery = selector({
  key: 'MyDBQuery',
  get: async () => {
    const response = await fetch(getMyRequestUrl());
    return response.json();
  },
});

function QueryResults() {
  const queryResults = useRecoilValue(myQuery);

  return <div>{queryResults.foo}</div>;
}

function ResultsSection() {
  return (
    <React.Suspense fallback={<div>Loading...</div>}>
      <QueryResults />
    </React.Suspense>
  );
}

동기 예시

const tempFahrenheit = atom({
  key: 'tempFahrenheit',
  default: 32,
});

const tempCelcius = selector({
  key: 'tempCelcius',
  get: ({get}) => ((get(tempFahrenheit) - 32) * 5) / 9,
  set: ({set}, newValue) =>
    set(
      tempFahrenheit,
      newValue instanceof DefaultValue ? newValue : (newValue * 9) / 5 + 32,
    ),
});

function TempCelcius() {
  const [tempF, setTempF] = useRecoilState(tempFahrenheit);
  const [tempC, setTempC] = useRecoilState(tempCelcius);
  const resetTemp = useResetRecoilState(tempCelcius);

  const addTenCelcius = () => setTempC(tempC + 10);
  const addTenFahrenheit = () => setTempF(tempF + 10);
  const reset = () => resetTemp();

  return (
    <div>
      Temp (Celcius): {tempC}
      <br />
      Temp (Fahrenheit): {tempF}
      <br />
      <button onClick={addTenCelcius}>Add 10 Celcius</button>
      <br />
      <button onClick={addTenFahrenheit}>Add 10 Fahrenheit</button>
      <br />
      <button onClick={reset}>Reset</button>
    </div>
  );
}

참조
Recoil 레퍼런스 문서
https://recoiljs.org/ko/docs/api-reference/core/selector

0개의 댓글