useAtom
useAtom
useAtom
훅은 상태에서 아톰을 읽어오는 데 사용됩니다.
상태는 아톰 설정과 아톰 값으로 이루어진 WeakMap으로 볼 수 있습니다.
useAtom
훅은 React의 useState
와 마찬가지로, 아톰 값과 업데이트 함수를 튜플 형태로 반환합니다.
이 훅은 atom()
로 생성된 아톰 설정을 인자로 받습니다.
아톰 설정이 생성될 때는 아직 값이 연결되어 있지 않습니다.
useAtom
을 통해 아톰이 사용되면 초기값이 상태에 저장됩니다.
만약 아톰이 파생된 아톰이라면, 초기값을 계산하기 위해 읽기 함수가 호출됩니다.
아톰이 더 이상 사용되지 않으면, 즉 해당 아톰을 사용하는 모든 컴포넌트가 언마운트되고 아톰 설정이 더 이상 존재하지 않으면, 상태의 값은 가비지 컬렉션됩니다.
const [value, setValue] = useAtom(anAtom)
setValue
는 하나의 인자를 받으며, 이 인자는 아톰의 쓰기 함수에 세 번째 매개변수로 전달됩니다.
최종 결과는 쓰기 함수가 어떻게 구현되었는지에 따라 달라집니다.
쓰기 함수가 명시적으로 설정되지 않았다면, 아톰은 setValue
에 전달된 값을 그대로 받습니다.
참고: atom 섹션에서 언급했듯이, 아톰을 생성할 때 참조 동등성(referential equality)이 중요합니다.
이를 제대로 처리하지 않으면 무한 루프가 발생할 수 있습니다.
const stableAtom = atom(0)const Component = () => {const [atomValue] = useAtom(atom(0)) // 이 코드는 매 렌더링마다 아톰 인스턴스가 재생성되므로 무한 루프를 일으킵니다.const [atomValue] = useAtom(stableAtom) // 이 코드는 문제없이 동작합니다.const [derivedAtomValue] = useAtom(useMemo(// 이 코드도 문제없이 동작합니다.() => atom((get) => get(stableAtom) * 2),[],),)}
참고: React는 컴포넌트를 호출하는 역할을 담당하므로, 컴포넌트는 멱등성(idempotent)을 가져야 하며 여러 번 호출될 준비가 되어 있어야 합니다.
프롭스나 아톰이 변경되지 않았더라도 추가 리렌더링이 발생하는 경우가 많습니다.
커밋 없이 발생하는 추가 리렌더링은 React 18의 useReducer의 기본 동작이므로 예상된 현상입니다.
Signatures
// 원시적이거나 쓰기 가능한 파생 아톰function useAtom<Value, Update>(atom: WritableAtom<Value, Update>,options?: { store?: Store },): [Value, SetAtom<Update>]// 읽기 전용 아톰function useAtom<Value>(atom: Atom<Value>,options?: { store?: Store },): [Value, never]
아톰 의존성 작동 방식
"read" 함수를 호출할 때마다 의존성과 의존 대상(dependents)을 새로 고칩니다.
read 함수는 아톰의 첫 번째 매개변수입니다. B가 A에 의존한다는 것은 A가 B의 의존성이고, B가 A의 의존 대상임을 의미합니다.
const uppercaseAtom = atom((get) => get(textAtom).toUpperCase())
아톰을 생성할 때는 의존성이 존재하지 않습니다. 처음 사용할 때 read 함수를 실행하고 uppercaseAtom
이 textAtom
에 의존한다는 결론을 내립니다. 따라서 uppercaseAtom
은 textAtom
의 의존 대상에 추가됩니다.
uppercaseAtom
의 read 함수를 다시 실행할 때(textAtom
의존성이 업데이트되었기 때문에), 의존성이 다시 생성됩니다. 이 경우 동일한 의존성이 생성됩니다. 그런 다음 textAtom
에서 오래된 의존 대상을 제거하고 최신 버전으로 대체합니다.
아톰은 필요할 때 생성할 수 있습니다
여기서 보여준 기본 예제들은 컴포넌트 외부에서 전역적으로 아톰을 정의하는 방법을 보여줍니다. 하지만 아톰을 어디서나, 언제든지 생성할 수 있습니다. 아 톰은 객체 참조 식별자로 구분된다는 점만 기억하면 됩니다.
렌더링 함수 내에서 아톰을 생성하려면 일반적으로 useRef
나 useMemo
같은 훅을 사용해 메모이제이션을 적용합니다. 그렇지 않으면 컴포넌트가 렌더링될 때마다 아톰이 다시 생성됩니다.
아톰을 생성하고 useState
에 저장하거나 다른 아톰에 저장할 수도 있습니다. 이슈 #5에서 예제를 확인할 수 있습니다.
아톰을 전역적으로 캐시할 수도 있습니다. 이 예제나 저 예제를 참고하세요.
매개변수화된 아톰을 사용하려면 유틸리티의 atomFamily
를 확인하세요.
useAtomValue
const countAtom = atom(0)const Counter = () => {const setCount = useSetAtom(countAtom)const count = useAtomValue(countAtom)return (<><div>count: {count}</div><button onClick={() => setCount(count + 1)}>+1</button></>)}
useSetAtom
훅과 유사하게, useAtomValue
는 읽기 전용 아톰에 접근할 수 있게 해줍니다. 하지만 이 훅은 읽기-쓰기 아톰의 값에도 접근할 수 있습니다.
useSetAtom
const switchAtom = atom(false)const SetTrueButton = () => {const setCount = useSetAtom(switchAtom)const setTrue = () => setCount(true)return (<div><button onClick={setTrue}>Set True</button></div>)}const SetFalseButton = () => {const setCount = useSetAtom(switchAtom)const setFalse = () => setCount(false)return (<div><button onClick={setFalse}>Set False</button></div>)}export default function App() {const state = useAtomValue(switchAtom)return (<div>State: <b>{state.toString()}</b><SetTrueButton /><SetFalseButton /></div>)}
아톰의 값을 읽지 않고 업데이트해야 할 때 useSetAtom()
을 사용할 수 있습니다.
이 방법은 성능이 중요한 경우에 특히 유용합니다. const [, setValue] = useAtom(valueAtom)
을 사용하면 valueAtom
이 업데이트될 때마다 불필요한 리렌더링이 발생할 수 있기 때문입니다.