본 시리즈는 정재남님의 풀스택 리액트 라이브코딩 - 간단한 쇼핑몰 만들기 강의 내용을 기반으로, 추가적인 학습을 통해 습득한 지식 또는 강의 코드를 다른 방법으로 구현한 경험을 작성하고 있습니다. 강의 코드(GitHub)를 확인하세요.
Recoil은 Facebook에서 만든 React를 위한 상태 관리 라이브러리로, 단방향으로 상태를 전달한다.
<RecoilRoot>
는 하위 컴포넌트에게 recoil 상태를 사용할 수 있도록 제공하는 컴포넌트이다.<RecoilRoot>
는 recoil 상태를 공유하는 모든 컴포넌트들의 조상 컴포넌트여야 한다. -> 프로젝트의 root 컴포넌트에 제공하는 것이 효율적일 수 있다.<RecoilRoot>
가 여러 개 존재할 경우 상태 변경 사항이 다른 root와 공유되는 등 예기치 않은 동작이 발생할 수 있기 때문에 하나의 root만 사용할 것을 권장한다.selector
캐시같은 캐시들은 root 사이에서 공유될 수 있다. 이는 selector
함수를 사용하는 다른 컴포넌트에서 이미 생성된 캐시를 사용할 수 있다는 성능 향상의 장점이 있지만, root를 공유할 수 있다는 예측 불가능성이 있다.override
속성true
로, override
속성이 true
일 경우 해당 root는 새로운 recoil scope를 생성한다.override
속성이 false
일 경우override
속성을 false
로 하는 것은 recoil에서 제공하는 디버깅 및 개발 도구가 비활성화되므로 권장되지 않는다.override
속성을 false
로 해서 recoil 개발 도구를 비활성화 할 수 있다.atom
또는 selector
의 값이 변경될 때 개발자 도구에서 상태 변경 알림이 표시되지 않는다.export type RecoilRootProps =
| {
initializeState?: (mutableSnapshot: MutableSnapshot) => void;
override?: true;
children: React.ReactNode;
}
| {
override: false;
children: React.ReactNode;
};
/**
* Root component for managing Recoil state. Most Recoil hooks should be
* called from a component nested in a <RecoilRoot>
*/
export const RecoilRoot: React.FC<RecoilRootProps>;
atom
은 전역 상태로, 어떤 컴포넌트에서든 참조하고 사용할 수 있다.atom
을 참조하는 순간부터 컴포넌트는 atom
을 구독하고 있는 것으로, atom
값이 변경되면 atom
을 구독하고 있는 모든 컴포넌트는 리렌더링 된다.atom
의 반환값 RecoilState
이기 때문에 컴포넌트가 atom
을 참조할 때는 useRecoilState
메서드를 사용하여 인수에 atom
으로 선언된 state를 전달해야 한다.interface AtomOptionsWithoutDefault<T> {
key: NodeKey;
effects?: ReadonlyArray<AtomEffect<T>>;
effects_UNSTABLE?: ReadonlyArray<AtomEffect<T>>;
dangerouslyAllowMutability?: boolean;
}
interface AtomOptionsWithDefault<T> extends AtomOptionsWithoutDefault<T> {
default: RecoilValue<T> | Promise<T> | Loadable<T> | WrappedValue<T> | T;
}
export type AtomOptions<T> =
| AtomOptionsWithoutDefault<T>
| AtomOptionsWithDefault<T>;
/** 기본 atom: RecoilState */
export function atom<T>(options: AtomOptions<T>): RecoilState<T>;
/** 단순히 값을 감싸는데 사용되는 atom: WrappedValue */
export namespace atom {
function value<T>(value: T): WrappedValue<T>;
}
import { atom, useRecoilState } from "recoil";
const atomState = atom<defaultValueType>({
key: "uniqueKey",
default: "initValue",
});
const [state, setState] = useRecoilState(atomState);
selector
는 Derived state를 계산하는 데 사용되는 메서드로, 다른 atom
이나 selector
를 읽어들여 새로운 값을 계산하고 이 값을 다른 컴포넌트에서 사용할 수 있도록 해준다.selector
는 일반적인 상태값이 아닌, 읽기 전용(read-only)로 사용된다.selector
함수가 실행될 때 마다 selector
캐시가 생성되며 이 캐시는 메모리에 보관되어 재사용된다. 이는 selector
함수를 실행할 때마다 새로운 인스턴스를 만들 필요가 없어져 성능 향상에 도움이 된다.RecoilValueReadOnly
일 때 selector
값을 참조하기 위해서는 useRecoilValue
메서드를 사용해야 한다.export interface ReadOnlySelectorOptions<T> {
key: string;
get: (opts: {
get: GetRecoilValue;
getCallback: GetCallback;
}) => Promise<T> | RecoilValue<T> | Loadable<T> | WrappedValue<T> | T;
dangerouslyAllowMutability?: boolean;
cachePolicy_UNSTABLE?: CachePolicyWithoutEquality;
}
/** 읽기 전용(get)으로 작성되었을 때의 반환값: RecoilValueReadOnly */
export function selector<T>(
options: ReadOnlySelectorOptions<T>
): RecoilValueReadOnly<T>;
/** 읽기와 쓰기(get, set)가 모두 허용되었을 때의 반환값: RecoilState */
export function selector<T>(
options: ReadWriteSelectorOptions<T>
): RecoilState<T>;
/** 단순히 값을 감싸는데 사용되는 반환값: WrappedValue */
export namespace selector {
function value<T>(value: T): WrappedValue<T>;
}
selector
의 인수로 key
프로퍼티와 get
메세드를 가진 객체를 전달한다.get
메서드는 메서드 축약표현을 사용하지 않고 화살표 함수로 작성한다.selector
값을 참조하기 위해서는 useRecoilValue
메서드를 사용한다.import { selctor, useRecoilValue } from "recoil";
const mySelector = selector({
key: "mySelector",
get: ({ get }) => {
const value1 = get(myAtom1);
const value2 = get(myAtom2);
return value1 + value2;
},
});
export function Component() {
const derivedValue = useRecoilValue(mySelector);
return <div>derivedValue</div>;
}
selectorFamily
메서드는 key
프로퍼티와 get
, set
메서드를 가진 객체를 인수로 전달하면 selector
를 반환하는 메서드이다.selectorFamily
는 결과값이 RecoilState
이기 때문에 useRecoilState
메서드를 이용하여 selector
의 값을 참조해야 한다.selectorFamily
는 인수 작성 타입이 ReadWriteSelectorFamilyOptions
으로 get
, set
프로퍼티를 작성할 때 (param) => (options) => returnValue
방식으로 작성한다.selectorFamily
의 구조export interface ReadWriteSelectorFamilyOptions<
T,
P extends SerializableParam
> {
key: string;
get: (
param: P
) => (opts: {
get: GetRecoilValue;
getCallback: GetCallback;
}) => Promise<T> | Loadable<T> | WrappedValue<T> | RecoilValue<T> | T;
set: (param: P) => (
opts: {
set: SetRecoilState;
get: GetRecoilValue;
reset: ResetRecoilState;
},
newValue: T | DefaultValue
) => void;
cachePolicy_UNSTABLE?: CachePolicyWithoutEquality;
dangerouslyAllowMutability?: boolean;
}
export function selectorFamily<T, P extends SerializableParam>(
options: ReadWriteSelectorFamilyOptions<T, P>
): (param: P) => RecoilState<T>;
import { selectorFamily } from "recoil";
const mySelector = selectorFamily({
key: "mySelector",
get: ({ get }) => {
// ...
},
set: ({ get, set }, newValue) => {
// ...
},
});
function useRecoilState<T>(
recoilState: RecoilState<T>
): [T, SetterOrUpdater<T>];
atom
에 접근하고 관리하기 위한 메서드 (읽고 쓰기가 가능)atom
의 값을 구독하여 업데이트할 수 있는 hook으로, useState와 동일한 방식으로 사용할 수 있다.atom
에 컴포넌트를 등록하는 hook이다.useState
와 비슷하게 [recoilState, setRecoilState]
이다.function useRecoilValue<T>(recoilValue: RecoilValue<T>): T;
atom
을 읽기만 할 때 사용한다.atom
에 컴포넌트를 등록하는 hook이다. (atom이 변경되는 것을 구독한다.)function useSetRecoilState<T>(recoilState: RecoilState<T>): SetterOrUpdater<T>;
atom
값을 변경하는 등 쓰기만 할 때 사용한다.function useResetRecoilState(recoilState: RecoilState<any>): Resetter;
atom
을 초깃값으로 초기화할 때 사용한다.function useRecoilCallback<Args extends ReadonlyArray<unknown>, Return>(
fn: (interface: CallbackInterface) => (...args: Args) => Return,
deps?: ReadonlyArray<unknown>
): (...args: Args) => Return;
atom
에 등록하지 않고 값을 읽어야 하는 경우에 사용한다.