Redux 에선 비동기 데이터 쿼리를 위해 redux-thunk, redux-observable, redux-saga 등의 Middleware 를 사용해야 합니다.
Middleware 는 action 을 dispatch 하고 reducer 에서 상태 업데이트를 하기 전 비동기 처리(예: 네트워크 요청, setTimeout)를 하는 중간자 역할이라고 보면 됩니다.
Redux Middleware 는 비동기 처리를 위해 여전히 강력한 도구지만 이를 위해 추가되는 boilerplate 코드가 많아지는 것은 무시할 수 없습니다.
당연히도 앱의 규모가 커질 수록 복잡도가 늘어나고 코드 양이 더욱 방대해집니다.
이와는 다르게 Recoil 에선 동기 / 비동기 함수 모두 selector
에서 처리할 수 있습니다.
단, React 의 render() 함수가 동기이기 때문에 promise 가 resolve 되기 전에 렌더링 할 수가 없습니다.
이때 대기중인 데이터를 처리하기 위해 Recoil 은 React Suspense 와 함께 사용되도록 디자인되어있습니다.
아래의 예처럼 컴포넌트를 Suspense 로 감싸서 대기중인 하위 항목들을 잡아내고 fallback UI 를 대신 렌더합니다.
// atom.js
export const todoIdState = atom({
key: "todoIdState",
default: 1
});
export const todoItemQuery = selector({
key: "todoItemQuery",
get: async ({ get }) => {
const id = get(todoIdState);
const response = await axios.get(
`https://jsonplaceholder.typicode.com/todos/${id}`
);
return response.data;
}
});
// App.js
import { RecoilRoot } from "recoil";
import { Suspense } from "react";
import Container from "./container";
export default function App() {
return (
<RecoilRoot>
<Suspense fallback={() => <p>Loading...</p>}>
<Container />
</Suspense>
</RecoilRoot>
);
}
// container/index.js
import { todoItemQuery } from "../atom";
import { useRecoilValue } from "recoil";
const Container = () => {
const data = useRecoilValue(todoItemQuery);
return <div>{data.title}</div>;
};
export default Container;
파라미터에 따라 비동기 데이터 요청
파라미터를 기반으로 쿼리하고 싶을 땐 selectorFamily
를 사용할 수 있습니다.
// atom.js
import axios from 'axios';
import { selectorFamily } from 'recoil';
export const todoItemQuery = selectorFamily({
key: "todoItemQuery",
get: (id) => async () => {
const response = await axios.get(
`https://jsonplaceholder.typicode.com/todos/${id}`
);
return response.data;
}
});
// App.js
import { RecoilRoot } from "recoil";
import { Suspense } from "react";
import Container from "./container";
export default function App() {
return (
<RecoilRoot>
<Suspense fallback={() => <p>Loading...</p>}>
<Container id={1} />
</Suspense>
</RecoilRoot>
);
}
// container/index.js
import { todoItemQuery } from "../atom";
import { useRecoilValue } from "recoil";
const Container = ({ id }) => {
const data = useRecoilValue(todoItemQuery(id));
return <div>{data.title}</div>;
};
export default Container;