이 두 가지 특성이 트리의 최상단(state가 존재하는 곳)부터 트리의 말단(state가 사용되는 곳)까지의 코드 분할을 어렵게 한다.
Recoil을 사용하면 atoms (공유 상태)에서 selectors (순수 함수)를 거쳐 React 컴포넌트로 내려가는 data-flow graph를 만들 수 있다. Atoms는 컴포넌트가 구독할 수 있는 상태의 단위다. Selectors는 atoms 상태값을 동기 또는 비동기 방식을 통해 변환한다.
상태의 단위, 업데이트와 구독이 가능
atom이 업데이트 되면 각각의 구독된 컴포넌트는 새로운 값을 반영하여 다시 렌더링 된다.
atoms은 런타임에서 생성될 수도 있다. atoms는 로컬 컴포넌트 상태 대신 사용할 수 있다.
동일한 atom이 여러 컴포넌트에서 사용되는 경우 모든 컴포넌트는 상태를 공유한다.
import { atom } from 'recoil';
export default atom<string | undefined>({
key: 'QuizDifficulty',
default: undefined,
});
atom
function: { key: [unique string], default: [initial value] }
객체를 parameter로 받는 함수
import { useRecoilState } from 'recoil';
const [quizDifficulty, setQuizDifficulty] = useRecoilState(
QuizDifficultyState
);
useState()와 비슷하지만 상태가 컴포넌트 간에 공유될 수 있다는 차이가 있다.
Selector는 atoms나 다른 selectors를 입력으로 받아들이는 순수함수이다.
상위의 atoms 또는 selectors가 업데이트 되면 하위의 selector함수도 다시 실행된다. 컴포넌트들은 selectors를 atoms처럼 구독할 수 있으며 selectors가 변경되면 컴포넌트들도 다시 렌더링 된다.
전역 상태를 관리하기 위한 방법 atom이 하지 못하는 두가지를 할 수 있다.
atom을 구독하는 기능
이미 선언된 atom의 값이 변할때 그 atom을 구독하고 있다가 Selector가 다시 한 번 실행된다.
server와 통신해서 response 값을 가질 수 있다.
const initilaOrderState = selector<TResponseData>({
key: 'initilaOrderState',
get: async ({ get }) => {
// QueryDataState atom을 구독
const queryData = get(QueryDataState);
if (
queryData == undefined ||
window.location.pathname != `/${QUIZ_PAGENAME}`
)
return undefined;
const { amount, difficulty } = queryData;
const axios = customAxios();
const response = await axios({
method: 'GET',
params: {
amount,
difficulty,
type: 'multiple',
},
});
...
(decoding logic)
...
// 서버로 부터 받아온 response data를 rerurn
return decodedResponseData;
},
set: ({ get, set }) => {
const amount = get(QuizNumbersState);
const difficulty = get(QuizDifficultyState);
set(QueryDataState, { amount, difficulty });
set(QuizNumbersState, DEFAULT_NUMBERS);
set(QuizDifficultyState, undefined);
},
});
get
프로퍼티: 구독한 atom의 값이 변경 될 때 재실행 된다.
위의 코드의 경우 QueryDataState이 변경되면 서버로 부터 quiz 데이터를 받아오는 logic을 재실행한다.
set
프로퍼티: 자체적으로 setState()를 할 수없기 때문에 비동지적으로 setState()를 해주는 기능을 한다.
get()으로 atom의 값을 가져오고 set()함수를 통해 update한다.
update를 하게되면 selector가 구독하고 있으므로 atom의 변경을 인지하고 get프로퍼티의 기능을 수행한다.
set프로퍼티가 없으면 set을 할 수 없다.
selector 자체는 state 본체라고 할 수 없다. atom의 파편이다. 즉, atom을 무조건 구독해야한다.
만약 구독하지 않더라도 비동기 function
이기 때문에 setState()로 새롭게 수정하는 로직을 실행할 수 없다.
import { useResetRecoilState } from 'recoil';
selector로 선언된 global state를 reset한다.
const resetIntialProps = useResetRecoilState(InitialPropsState);
<Suspense fallback={<ShimmerPage />}>
<Switch>
<Route path={`/${QUIZ_PAGENAME}`}>
<Helmet title="Quiz page" />
<QuizPage /> // 비동기 데이터를 받는 컴포넌트
</Route>
...
</Switch>
</Suspense>
const initialProps = useRecoilValue(InitialPropsState);