JotaiJotai

상태
React를 위한 기본적이고 유연한 상태 관리

Async

비동기 아톰을 사용하면 실제 데이터에 접근할 수 있으면서도 아톰에서 직접 관리할 수 있고, 이를 매우 쉽게 처리할 수 있습니다.

이를 두 가지 주요 범주로 나눌 수 있습니다:

  • 비동기 읽기 아톰: 값을 가져오려고 시도하는 즉시 비동기 요청이 시작됩니다. 이를 "스마트 게터"라고 생각할 수 있습니다.
  • 비동기 쓰기 아톰: 특정 순간에 비동기 요청이 시작됩니다. 이를 "액션"이라고 생각할 수 있습니다.

비동기 읽기 아톰

아톰의 read 함수는 Promise를 반환할 수 있습니다.

const countAtom = atom(1)
const asyncAtom = atom(async (get) => get(countAtom) * 2)

Jotai는 기본적으로 비동기 흐름을 처리하기 위해 Suspense를 활용합니다.

const ComponentUsingAsyncAtoms = () => {
const [num] = useAtom(asyncAtom)
// 여기서 `num`은 항상 `number` 타입입니다. asyncAtom이 Promise를 반환하더라도 말이죠.
}
const App = () => {
return (
<Suspense fallback={/* 서스펜드 중에 보여줄 내용 */}>
<ComponentUsingAsyncAtoms />
</Suspense>
)
}

또는, Jotai가 자동으로 처리하는 서스펜드를 피하고 싶다면, 아톰을 loadable API로 감싸면 됩니다.

다른 아톰이 비동기 아톰을 사용한다면, Promise를 반환합니다. 따라서 해당 아톰도 비동기로 만들어야 합니다.

const anotherAtom = atom(async (get) => (await get(asyncAtom)) / 2)

이것은 쓰기 함수가 있는 아톰에도 적용됩니다.

const asyncAtom = atom(async (get) => ...)
const writeAtom = atom(null, async (get, set, payload) => {
await get(asyncAtom)
// ...
})

비동기 쓰기 아톰

비동기 쓰기 아톰은 또 다른 종류의 비동기 아톰입니다. 아톰의 write 함수가 Promise를 반환할 때 사용합니다.

const countAtom = atom(1)
const asyncIncrementAtom = atom(null, async (get, set) => {
// 무언가를 기다림
set(countAtom, get(countAtom) + 1)
})
const Component = () => {
const [, increment] = useAtom(asyncIncrementAtom)
const handleClick = () => {
increment()
}
// ...
}

비동기 처리의 유연성

Jotai를 사용하면 비동기에서 동기로 전환하여 원할 때 Suspense를 트리거할 수 있는 흥미로운 패턴을 구현할 수 있습니다.

const request = async () => fetch('https://...').then((res) => res.json())
const baseAtom = atom(0)
const Component = () => {
const [value, setValue] = useAtom(baseAtom)
const handleClick = () => {
setValue(request()) // 요청이 완료될 때까지 Suspense가 발생
}
// ...
}

TypeScript에서 사용하기

TypeScript에서 atom(0)PrimitiveAtom<number>로 추론됩니다. 이 경우 Promise<number>를 값으로 받을 수 없기 때문에 앞의 코드는 타입 검사를 통과하지 못합니다. 이를 해결하려면 아톰을 명시적으로 타입 지정하고 Promise<number>를 허용하는 값으로 추가해야 합니다.

const baseAtom = atom<number | Promise<number>>(0) // 동기 및 비동기 값을 모두 허용

Async forever

때로는 예측할 수 없는 시점(또는 영원히)까지 실행을 중단하고 싶을 수 있습니다.

const baseAtom = atom(new Promise(() => {})) // 다른 값이 설정될 때까지 실행이 중단됩니다

Suspense

Jotai는 비동기 지원을 퍼스트클래스 기능으로 제공합니다. Jotai는 React Suspense를 핵심적으로 활용합니다.

기술적으로 React 17에서는 React.lazy 이외의 Suspense 사용이 아직 지원되지 않거나 문서화되지 않았습니다. 이 문제가 블로킹 요소라면, loadable API를 사용하여 Suspense를 피할 수 있습니다.

비동기 아톰을 사용하려면 컴포넌트 트리를 <Suspense>로 감싸야 합니다.

<Provider>를 사용하는 경우, <Provider> 내부에 최소 하나 이상<Suspense>를 배치해야 합니다. 그렇지 않으면 컴포넌트를 렌더링하는 동안 무한 루프가 발생할 수 있습니다.

const App = () => (
<Provider>
<Suspense fallback="Loading...">
<Layout />
</Suspense>
</Provider>
)

컴포넌트 트리 내에 더 많은 <Suspense>를 배치하는 것도 가능하며, Jotai의 내장된 처리를 최대한 활용하기 위해 고려해야 합니다.