본 시리즈는 정재남님의 풀스택 리액트 라이브코딩 - 간단한 쇼핑몰 만들기 강의 내용을 기반으로, 추가적인 학습을 통해 습득한 지식 또는 강의 코드를 다른 방법으로 구현한 경험을 작성하고 있습니다. 강의 코드(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에 등록하지 않고 값을 읽어야 하는 경우에 사용한다.