JotaiJotai

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

v2 API migration

RFC: https://github.com/pmndrs/jotai/discussions/1514

Jotai v1은 2022년 6월에 출시되었으며, 다양한 피드백을 받았습니다. React도 Promise에 대한 퍼스트클래스 지원을 제안하고 있습니다. Jotai v2에는 새로운 API가 추가될 예정입니다.

안타깝게도, 새로운 기능과 함께 몇 가지 호환성이 깨지는 변경 사항이 있습니다.

What are new features

바닐라 라이브러리

Jotai는 바닐라(React를 사용하지 않는) 함수와 React 함수를 별도로 제공합니다. 이들은 jotai/vanilla와 같은 별도의 진입점에서 제공됩니다.

Store API

Jotai는 스토어 인터페이스를 제공하여 여러분이 직접 atom 값을 조작할 수 있게 합니다.

import { createStore } from 'jotai' // 또는 'jotai/vanilla'에서 가져올 수 있습니다.
const store = createStore()
store.set(fooAtom, 'foo')
console.log(store.get(fooAtom)) // "foo"를 출력합니다.
const unsub = store.sub(fooAtom, () => {
console.log('스토어 내 fooAtom 값이 변경되었습니다.')
})
// unsub()를 호출하여 구독을 해제할 수 있습니다.

또한, 여러분만의 React Context를 생성하여 스토어를 전달할 수도 있습니다.

더 유연한 아톰 write 함수

write 함수는 여러 개의 인자를 받을 수 있고, 값을 반환할 수도 있습니다.

atom(
(get) => get(...),
(get, set, arg1, arg2, ...) => {
...
return someValue
}
)

What are breaking

비동기 아톰은 더 이상 특별하지 않습니다

비동기 아톰은 단순히 Promise 값을 가진 일반 아톰입니다.
아톰의 getter 함수는 Promise를 해결하지 않습니다.
반면에 useAtom 훅은 계속해서 Promise를 해결합니다.

splitAtom과 같은 일부 유틸리티는 동기 아톰을 기대하며,
비동기 아톰에서는 작동하지 않습니다.

Writable atom 타입 변경 (TypeScript 전용)

// 이전
WritableAtom<Value, Arg, Result extends void | Promise<void>>
// 변경 후
WritableAtom<Value, Args extends unknown[], Result>

일반적으로 WritableAtom 타입을 직접 사용하는 것은 피하는 것이 좋습니다.

일부 기능이 제거됨

  • Provider의 initialValues prop이 제거되었습니다. store가 더 유연하기 때문입니다.
  • Provider의 scope props가 제거되었습니다. 여러분이 직접 컨텍스트를 생성할 수 있기 때문입니다.
  • abortableAtom 유틸리티가 제거되었습니다. 해당 기능이 기본적으로 포함되었기 때문입니다.
  • waitForAll 유틸리티가 제거되었습니다. Promise.all로 충분히 동작하기 때문입니다.

Migration guides

비동기 아톰

비동기 아톰의 get 함수는 프로미스를 자동으로 해결하지 않습니다. 따라서 await.then()을 사용해야 합니다.

간단히 말해, 변경 사항은 다음과 같습니다.
(타입스크립트 사용자라면 타입이 어디를 변경해야 하는지 알려줄 것입니다.)

이전 API
const asyncAtom = atom(async () => 'hello')
const derivedAtom = atom((get) => get(asyncAtom).toUppercase())
새로운 API
const asyncAtom = atom(async () => 'hello')
const derivedAtom = atom(async (get) => (await get(asyncAtom)).toUppercase())
// 또는
const derivedAtom = atom((get) => get(asyncAtom).then((x) => x.toUppercase()))

Provider's initialValues prop

Previous API_nFpo6E2VMy6fLJpU4Lxsqk
const countAtom = atom(0)
// 컴포넌트 내부
<Provider initialValues={[[countAtom, 1]]}>
...
const countAtom = atom(0)
const HydrateAtoms = ({ initialValues, children }) => {
useHydrateAtoms(initialValues)
return children
}
// 컴포넌트 내부
<Provider>
<HydrateAtoms initialValues={[[countAtom, 1]]}>
...

Provider's scope prop

const myScope = Symbol()
// 부모 컴포넌트
<Provider scope={myScope}>
...
</Provider>
// 자식 컴포넌트
useAtom(..., myScope)
const MyContext = createContext()
const store = createStore()
// 부모 컴포넌트
<MyContext.Provider value={store}>
...
</MyContext.Provider>
// 자식 컴포넌트
const store = useContext(MyContext)
useAtom(..., { store })

abortableAtom 유틸리티

이제 일반 atom에서 지원되기 때문에, 더 이상 이전의 abortableAtom 유틸리티가 필요하지 않습니다.

Previous API_K7oqQc9QLUx8NhEdMYuHaG
const asyncAtom = abortableAtom(async (get, { signal }) => {
...
}
New API_iLthHtGNnNmpbKuw9s58a8
const asyncAtom = atom(async (get, { signal }) => {
...
}

waitForAll 유틸리티

이제 더 이상 이전의 waitForAll 유틸리티가 필요하지 않습니다.
네이티브 Promise API를 사용할 수 있기 때문입니다.

Previous API_haTiJuqET3bCzvaaR33qws
const allAtom = waitForAll([fooAtom, barAtom])
New API_Ys4YeF6YELZqn3rsfEfMJS
const allAtom = atom((get) => Promise.all([get(fooAtom), get(barAtom)]))

렌더링 함수 내에서 아톰을 생성하면 무한 루프가 발생할 수 있으니 주의하세요.

splitAtom 유틸리티 (또는 다른 유틸리티)와 비동기 아톰

splitAtom 유틸리티는 동기 아톰만 받을 수 있습니다.
비동기 아톰을 전달하기 전에 먼저 동기 아톰으로 변환해야 합니다.

이 규칙은 jotai-tanstack-queryatomsWithQuery와 같은 다른 유틸리티에도 적용됩니다.

Previous API_Ei5CDD8JTheDBuj9pCiQWm
const splittedAtom = splitAtom(asyncArrayAtom)
New API_i9GyS5EDVp4P7oBsgdk3LZ
const splittedAtom = splitAtom(unwrap(asyncArrayAtom, () => []))

현재 unwrap은 불안정하며 문서화되지 않았습니다.
대신 loadable을 사용하면 로딩 상태를 더 잘 제어할 수 있습니다.
<Suspense>를 사용해야 한다면, atoms-in-atom 패턴이 도움이 될 수 있습니다.

자세한 내용은 다음 토론을 참고하세요:

Some other changes

유틸리티

  • atomWithStorage 유틸리티의 delayInit 옵션이 기본값으로 제거되었습니다. 또한 첫 렌더링 시에는 항상 initialValue를 렌더링하고, 이후 렌더링에서는 저장된 값이 있다면 그 값을 렌더링합니다. 이 새로운 동작은 v1과 다릅니다. 자세한 내용은 https://github.com/pmndrs/jotai/discussions/1737을 참고하세요.
  • useHydrateAtoms는 이제 쓰기 가능한(writable) 아톰만 받을 수 있습니다.

Import 문

v2 API는 라이브러리 개발자와 React를 사용하지 않는 사용자를 위해 대체 진입점에서도 제공됩니다.

  • jotai/vanilla
  • jotai/vanilla/utils
  • jotai/react
  • jotai/react/utils
// v1.11.0부터 사용 가능
import { atom } from 'jotai/vanilla'
import { useAtom } from 'jotai/react'
// v2.0.0부터 사용 가능
import { atom } from 'jotai' // 'jotai/vanilla'와 동일
import { useAtom } from 'jotai' // 'jotai/react'와 동일

참고: ESM을 사용하지 않는다면, 트리 쉐이킹을 더 잘 활용하기 위해 jotai 대신 jotai/vanilla 등을 사용하는 것이 좋습니다.