말 그대로 selector는 기본적으로 값을 자체적으로 캐싱한다.
만약 입력된 적이 있는 값이라면 그 값을 기억하고, 이 값이 다시 호출되면 이전에 캐싱된 결과를 보여주기 때문에 비동기 데이터를 다루는 측면에서 유리하다.
유저데이터 같은 데이터는 애플리케이션을 만들 때 많은 컴포넌트에서 사용되기 때문에 전역 상태로 관리하면 좋다.
// userAtom.js
import axios from "axios";
import { atom, selector } from "recoil";
// 현재 유저의 id state
export const currentUserIdState = atom({
key:'currentUserIdState',
default: 1,
})
// 비동기 요청 보내기
export const currentUserNameQuery = selector({
key:"currentUserName",
get : async ({get}) => {
const path = "https://jsonplaceholder.typicode.com/users"
const response = await axios.get(`${path}${get(currentUserIdState)}`)
return response.data.name
}
})
만약 user의 이름이 쿼리해야 하는 데이터베이스에 저장되어 있었다면, Promise를 리턴하거나 혹은 async 함수를 사용하기만 하면 된다. 의존성에 하나라도 변경점이 생긴다면, selector는 새로운 쿼리를 재평가하고 다시 실행시킨다. 그리고 결과는 쿼리가 유니크한 인풋이 있을때에만 실행되도록 캐시된다.
// App.js
function CurrentUserInfo(){
const userName = useRecoilValue(currentUserNameQuery)
return <div>{userName}</div>
}
React 렌더함수는 동기인데, promise가 resolve 되기 전에 무엇을 렌더할 수 있을까?
Recoil은 보류중인 데이터를 다루기 위해 React Suspense와 함께 동작하도록 디자인 되어있다. 컴포넌트를 Suspense의 경계로 감싸는 걸로 아직 보류중인 하위 항목들을 잡아내고 대체하기 위한 UI를 렌더한다.
<React.Suspense fallback={<div>...loading</div>}>
<CurrentUserInfo />
</React.Suspense>