atom
atom
atom
함수는 아톰 설정을 생성합니다.
이를 "아톰 설정"이라고 부르는 이유는 단순히 정의일 뿐이며 아직 값을 가지고 있지 않기 때문입니다.
문맥이 명확하다면 그냥 "아톰"이라고 부르기도 합니다.
아톰 설정은 불변 객체입니다. 아톰 설정 객체는 값을 가지고 있지 않습니다. 아톰 값은 스토어에 존재합니다.
기본 아톰(설정)을 생성하려면 초기값만 제공하면 됩니다.
import { atom } from 'jotai'const priceAtom = atom(10)const messageAtom = atom('hello')const productAtom = atom({ id: 12, name: 'good stuff' })
파생 아톰도 생성할 수 있습니다. 세 가지 패턴이 있습니다:
- 읽기 전용 아톰
- 쓰기 전용 아톰
- 읽기-쓰기 아톰
파생 아톰을 생성하려면 읽기 함수와 선택적으로 쓰기 함수를 전달합니다.
const readOnlyAtom = atom((get) => get(priceAtom) * 2)const writeOnlyAtom = atom(null, // 첫 번째 인자로 `null`을 전달하는 것이 관례입니다.(get, set, update) => {// `update`는 이 아톰을 업데이트하기 위해 받는 단일 값입니다.set(priceAtom, get(priceAtom) - update.discount)// 또는 두 번째 매개변수로 함수를 전달할 수도 있습니다.// 이 함수는 호출되며, 첫 번째 매개변수로 아톰의 현재 값을 받습니다.set(priceAtom, (price) => price - update.discount)},)const readWriteAtom = atom((get) => get(priceAtom) * 2,(get, set, newPrice) => {set(priceAtom, newPrice / 2)// 동시에 원하는 만큼 많은 아톰을 설정할 수 있습니다.},)
읽기 함수의 get
은 아톰 값을 읽기 위한 것입니다.
이것은 반응적이며 읽기 종속성이 추적됩니다.
쓰기 함수의 get
도 아톰 값을 읽기 위한 것이지만 추적되지 않습니다.
또한 Jotai v1 API에서는 해결되지 않은 비동기 값을 읽을 수 없습니다.
쓰기 함수의 set
은 아톰 값을 쓰기 위한 것입니다.
이것은 대상 아톰의 쓰기 함수를 호출합니다.
렌더 함수에서 atom 생성 시 주의사항
atom 설정은 어디서든 생성할 수 있지만, 참조 동일성(referential equality)이 중요합니다.
동적으로 생성하는 것도 가능합니다.
렌더 함수에서 atom을 생성할 때는 안정적인 참조를 얻기 위해 useMemo
나 useRef
가 필요합니다.
메모이제이션을 위해 useMemo
와 useRef
중 어떤 것을 사용할지 확실하지 않다면, useMemo
를 사용하세요.
그렇지 않으면 useAtom
과 함께 사용할 때 무한 루프가 발생할 수 있습니다.
const Component = ({ value }) => {const valueAtom = useMemo(() => atom({ value }), [value])// ...}
시그니처
// 기본 아톰function atom<Value>(initialValue: Value): PrimitiveAtom<Value>// 읽기 전용 아톰function atom<Value>(read: (get: Getter) => Value): Atom<Value>// 쓰기 가능한 파생 아톰function atom<Value, Args extends unknown[], Result>(read: (get: Getter) => Value,write: (get: Getter, set: Setter, ...args: Args) => Result,): WritableAtom<Value, Args, Result>// 쓰기 전용 파생 아톰function atom<Value, Args extends unknown[], Result>(read: Value,write: (get: Getter, set: Setter, ...args: Args) => Result,): WritableAtom<Value, Args, Result>
initialValue
: 아톰의 값이 변경되기 전까지 반환되는 초기값.read
: 아톰이 읽힐 때마다 평가되는 함수.read
의 시그니처는(get) => Value
이며,get
은 아톰 설정을 받아 Provider에 저장된 값을 반환하는 함수. 의존성이 추적되므로,get
이 아톰에 대해 최소 한 번 사용되면 아톰 값이 변경될 때마다read
가 재평가됨.write
: 주로 아톰의 값을 변경하는 데 사용되는 함수. 더 자세히 설명하자면,useAtom
이 반환하는 쌍의 두 번째 값인useAtom()[1]
을 호출할 때마다 이 함수가 호출됨. 기본 아톰에서 이 함수의 기본값은 해당 아톰의 값을 변경함.write
의 시그니처는(get, set, ...args) => Result
.get
은 위에서 설명한 것과 유사하지만 의존성을 추적하지 않음.set
은 아톰 설정과 새로운 값을 받아 Provider에서 아톰 값을 업데이트하는 함수....args
는useAtom()[1]
을 호출할 때 받는 인자들.Result
는write
함수의 반환값.
const primitiveAtom = atom(initialValue)const derivedAtomWithRead = atom(read)const derivedAtomWithReadWrite = atom(read, write)const derivedAtomWithWriteOnly = atom(null, write)
아톰에는 두 가지 종류가 있음: 쓰기 가능한 아톰과 읽기 전용 아톰. 기본 아톰은 항상 쓰기 가능함. 파생 아톰은 write
가 지정된 경우에만 쓰기 가능함. 기본 아톰의 write
는 React.useState
의 setState
와 동일함.
debugLabel
속성
생성된 아톰 설정에는 선택적 속성인 debugLabel
을 추가할 수 있습니다. 디버그 레이블은 디버깅 시 아톰을 표시하는 데 사용됩니다. 자세한 내용은 디버깅 가이드를 참고하세요.
참고: 디버그 레이블은 반드시 고유할 필요는 없지만, 일반적으로 구분 가능하도록 만드는 것이 좋습니다.
onMount
속성
생성된 아톰 설정에는 선택적 속성인 onMount
를 포함할 수 있습니다. onMount
는 setAtom
함수를 인자로 받고, 선택적으로 onUnmount
함수를 반환하는 함수입니다.
onMount
함수는 아톰이 프로바이더에서 처음으로 구독될 때 호출되며, onUnmount
는 더 이상 구 독되지 않을 때 호출됩니다. React strict mode와 같은 경우, 아톰이 언마운트된 후 즉시 다시 마운트될 수 있습니다.
const anAtom = atom(1)anAtom.onMount = (setAtom) => {console.log('atom is mounted in provider')setAtom(c => c + 1) // 마운트 시 카운트 증가return () => { ... } // 선택적 onUnmount 함수 반환}const Component = () => {// 다음 경우에 컴포넌트가 마운트될 때 `onMount`가 호출됨:useAtom(anAtom)useAtomValue(anAtom)// 그러나 다음 경우에는 아톰이 구독되지 않으므로// `onMount`가 호출되지 않음:useSetAtom(anAtom)useAtomCallback(useCallback((get) => get(anAtom), []),)// ...}
setAtom
함수를 호출하면 아톰의 write
가 실행됩니다. write
를 커스터마이징하면 동작을 변경할 수 있습니다.
const countAtom = atom(1)const derivedAtom = atom((get) => get(countAtom),(get, set, action) => {if (action.type === 'init') {set(countAtom, 10)} else if (action.type === 'inc') {set(countAtom, (c) => c + 1)}},)derivedAtom.onMount = (setAtom) => {setAtom({ type: 'init' })}
고급 API
Jotai v2부터 read
함수는 두 번째 인자로 options
를 받습니다.
options.signal
이 옵션은 AbortController를 사용하여 비동기 함수를 중단할 수 있게 해줍니다. 새로운 계산(read
함수 호출)이 시작되기 전에 중단이 트리거됩니다.
사용 방법:
const readOnlyDerivedAtom = atom(async (get, { signal }) => {// signal을 사용하여 함수를 중단할 수 있음})const writableDerivedAtom = atom(async (get, { signal }) => {// signal을 사용하여 함수를 중단할 수 있음},(get, set, arg) => {// ...},)
signal
값은 AbortSignal입니다. signal.aborted
불리언 값을 확인하거나, addEventListener
와 함께 abort
이벤트를 사용할 수 있습니다.
fetch
사용 사례에서는 간단히 signal
을 전달할 수 있습니다.
아래 예제에서 fetch
사용법을 확인할 수 있습니다.
options.setSelf
이 함수는 self atom의 쓰기 함수를 호출하는 특별한 함수입니다.
⚠️ 주로 내부 사용과 서드파티 라이브러리 개발자를 위해 제공됩니다. 동작을 이해하려면 소스 코드를 주의 깊게 읽어보세요. 주요 변경 사항(breaking/non-breaking)이 있는지 릴리스 노트를 확인하세요.
Stackblitz
import { Suspense } from 'react'import { atom, useAtom } from 'jotai'const userIdAtom = atom(1)const userAtom = atom(async (get, { signal }) => {const userId = get(userIdAtom)const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}?_delay=2000`,{ signal },)return response.json()})const Controls = () => {const [userId, setUserId] = useAtom(userIdAtom)return (<div>User Id: {userId}<button onClick={() => setUserId((c) => c - 1)}>이전</button><button onClick={() => setUserId((c) => c + 1)}>다음</button></div>)}const UserName = () => {const [user] = useAtom(userAtom)return <div>사용자 이름: {user.name}</div>}const App = () => (<><Controls /><Suspense fallback="로딩 중..."><UserName /></Suspense></>)export default App