firebase에서 데이터를 받아와서 recoil 초기값에 넣어주어야 했다. autyStudy에서는 date에 따라 값이 달라지기 때문에 ,date값에 따라 다른 값을 호출해야 했다. 그래서 처음에 알고 있던, state를 선언하고, selector get 으로 호출을 하기에는 그 이후, CRUD를 하기에 부적합하다고 판단했다. 그래서 atom을 선언할 당시, default값에 API로 호출한 값을 넣기 위해 고민을 하였다.
Recoil을 사용하면 atoms (공유 상태)에서 selectors (순수 함수)를 거쳐 React 컴포넌트로 내려가는 data-flow graph를 만들 수 있다. Atoms는 컴포넌트가 구독할 수 있는 상태의 단위다. Selectors는 atoms 상태값을 동기 또는 비동기 방식을 통해 변환한다.
react state를 다루는 방식과 비슷하여 진입장벽이 낮고, react에 최적화 되어있는 상태 라이브러리로, 현재 redux에 middleware까지 사용할 정도로 규모가 크지 않다고 생각하여 Recoil을 사용하게 되었다.
RecoilRoot로 component를 감싸줄 때 미리 선언된 initialValue값을 initializeState를 통해 초기 값을 설정해 줄 수 있다.
const initialValue =
JSON.parse(localStorage.getItem(CART_ITEM) as string) ?? {};
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<RecoilRoot initializeState={() => Object.assign(cartState, initialValue)}>
<App />
</RecoilRoot>
</React.StrictMode>
);
현재 문제점은 initialValue 값에 date 값을 통해 todolist 값들을 비동기로 호출을 해야하는데, initialValue값이 호출 되기 전에 RecoilRoot가 선언되어, 값이 제대로 들어가지 않는 다는 것이었다.
export const productsList = selector<Product[]>({
key: 'productsList',
get: async () => {
try {
const response = await fetch(productsURL);
return (await response.json()) || [];
} catch (error) {
console.log(`Error: \n${error}`);
return [];
}
},
});
default에 selector를 한번 더 사용해서, 비동기로 default 값을 설정할 수 있다.
export const todoState = atom<Todo[]>({
key: 'todoState',
default: selector({
key: 'todos/get',
get: async ({ get }) => {
const date = get(dateState);
const todos = await getTodos(date);
return todos as Todo[];
},
}),
});
이렇게 설정한다면 값을 불러올 때도 loadableValue를 통해서 불러 올 수있다. 자세한 내용은 다음 Suspense and React.lazy()를 살펴보자
todoState가 비동기로 값을 불러오는 state이다.
todoState값이 불러오는 동안에는 값을 []로 선언하고 Suspense를 통해 그 동안에는 fallback에 해당하는 컴포넌트가 실행된다. 그래서 skeleton UI를 구현할 때도 사용이 된다.
const todoItemLoadable = useRecoilValueLoadable(todoState);
const todos = useMemo(() => {
return todoItemLoadable?.state === 'hasValue'
? todoItemLoadable?.contents
: [];
}, [todoItemLoadable]);
return(
<Suspense fallback={<TodoListLoad />}>
<TodoList todos={todos} />
</Suspense>
)