jotai 이것저것
1. jotai 에서 snapshot 상태를 참조해야한다면?
- 리코일에서는 상태의 스냅샷을 참조할 수 있는데, 조타이에서는 직접적인 인터페이스는 제공하지 않는다.
- recoil callback 에서 스냅샷은 해당 callback 이 시작되는 시점의 스냅샷을 말함. (cb 실행 도중 다른 곳에서 값이 변경되어도 snapshot 은 여전히 cb이 시작된 시점의 상태를 유지함.) 즉, 비동기 작업 중 일관된 상태를 유지해야할 때 사용 되어야 할 수 있음
- jotai 에서 스냅샷처럼 구현하려면, 아래와 같이 필요한 값들을 액션 시작 시점에 저장하면 될듯.
const handleAction = useAtomCallback(useCallback(async (get, set) => {
// 필요한 값들을 액션 시작 시점에 저장
const initialState = {
user: get(userAtom),
cart: get(cartAtom),
// ...
}
await someAsyncOperation()
// 저장해둔 초기 상태 사용
console.log('Initial state was:', initialState)
}, []))
2. jotai 에서 어떤 인터페이스 써야하나
// 1. 단순 상태
const cartMapAtom = atom<Record<string, CartNode>>({})
// 2. 파생 상태 (recoil의 selector)
const cartCountAtom = atom((get) =>
Object.keys(get(cartMapAtom)).length
)
// 3. 매개변수 필요 (캐싱 불필요) (recoil의 ~family)
const getCartAtom = (cartId: string) => atom(
(get) => get(cartMapAtom)[cartId],
(get, set, newValue) => set(cartMapAtom, {
...get(cartMapAtom),
[cartId]: newValue
})
)
// 4. 매개변수 필요 (캐싱 필요) ) (recoil의 ~family)
const cartAtomFamily = atomFamily((cartId: string) => atom(
(get) => get(cartMapAtom)[cartId],
(get, set, newValue) => set(cartMapAtom, {
...get(cartMapAtom),
[cartId]: newValue
})
))
atomFamily는 다음과 같은 경우사용하기.
- 같은 매개변수로 여러 번 호출될 때
- 컴포넌트가 자주 마운트/언마운트될 때
- 메모리 관리가 중요할 때
즉, atomFamily는 atom 을 param 별로 캐싱하기 위한 일종의 팩토리.
대신, atomFamily 는 param 을 key 로 하는 캐시 맵을 생성하기 때문에, 적절히 캐시를 지워주지 않을 경우 메모리 누수가 발생할 수 있음.
따라서, param 이 자주 변경되는 경우에는 사용하면 좋지 않음.
3. jotai 의 캐싱 시스템
- 일반 atom 은 의존성 변경 시에만 재계산 됨.
- atomWithCache 는 의존성 변경 시 + 같은 입력 값에 대한 결과를 재사용함
그리고 이 둘은 컴포넌트 생명주기와는 독립적임.
function useNetworkOnlyAtom<T>(atom: Atom<T>) {
const [value] = useAtom(atom)
const [, refresh] = useAtom(refreshAtom)
useEffect(() => {
refresh()
}, [])
return [value, refresh] as const
}