Initializing state on render
초기 렌더링 시 상태 초기화하기
아톰 상태를 초기 렌더링 시에 초기화하는 방법
아톰 상태를 초기 렌더링 시에 초기화하려면 default
프로퍼티를 사용합니다. 이 프로퍼티는 아톰이 처음 생성될 때 초기값을 설정합니다.
const countAtom = atom({key: 'count',default: 0,});
위 예제에서 countAtom
은 초기값으로 0
을 가집니다. 이 아톰을 사용하는 컴포넌트가 처음 렌더링될 때, 상태는 0
으로 초기화됩니다.
동적 초기값 설정하기
초기값을 동적으로 설정해야 하는 경우, default
프로퍼티에 함수를 전달할 수 있습니다. 이 함수는 아톰이 처음 생성될 때 호출되며, 반환값이 초기값으로 사용됩니다.
const dynamicCountAtom = atom({key: 'dynamicCount',default: () => {const initialValue = localStorage.getItem('count') || 0;return Number(initialValue);},});
이 예제에서는 localStorage
에서 값을 가져와 초기값으로 설정합니다. 이 방법을 사용하면 초기값을 런타임에 동적으로 결정할 수 있습니다.
초기 렌더링 시 상태 초기화 주의사항
- 아톰의 초기값은 컴포넌트의 첫 렌더링 시에만 설정됩니다. 이후 리렌더링에서는 초기값이 다시 설정되지 않습니다.
- 동적 초기값을 사용할 때는 함수가 순수해야 합니다. 부수 효과(side effect)가 발생하지 않도록 주의해야 합니다.
- 아톰의 초기값은 모든 컴포넌트에서 공유됩니다. 특정 컴포넌트에서만 다른 초기값을 사용하려면 새로운 아톰을 생성해야 합니다.
이러한 방법들을 활용하면 초기 렌더링 시 아톰 상태를 효과적으로 초기화할 수 있습니다.
여러분은 아톰을 사용하는 재사용 가능한 컴포넌트를 만들어야 할 때가 있습니다.
이 아톰들의 초기 상태는 컴포넌트에 전달된 props에 의해 결정됩니다.
아래는 Provider
와 initialValues
prop을 사용하여 상태를 초기화하는 기본 예제입니다.
기본 예제
CodeSandbox 링크: codesandbox.
일반 텍스트를 표시하고 업데이트할 수 있는 재사용 가능한 TextDisplay
컴포넌트를 생각해 보세요.
이 컴포넌트에는 PrettyText
와 UpdateTextInput
이라는 두 개의 자식 컴포넌트가 있습니다.
PrettyText
는 텍스트를 파란색으로 표시합니다.UpdateTextInput
은 텍스트 값을 업데이트하는 입력 필드입니다.
두 자식 컴포넌트에 text
를 prop으로 전달하는 대신, text
상태를 아톰으로 공유하기로 결정했습니다.
TextDisplay
컴포넌트를 재사용 가능하게 만들기 위해, initialTextValue
prop을 받아 text
아톰의 초기 상태를 결정합니다.
initialTextValue
를 textAtom
에 연결하기 위해, 새로운 스토어를 생성하고 이를 Provider
컴포넌트에 전달하는 컴포넌트로 자식 컴포넌트를 감쌉니다.
const textAtom = atom('')const PrettyText = () => {const [text] = useAtom(textAtom)return (<><textstyle={{color: 'blue',}}>{text}</text></>)}const UpdateTextInput = () => {const [text, setText] = useAtom(textAtom)const handleInputChange = (e) => {setText(e.target.value)}return (<><input onChange={handleInputChange} value={text} /></>)}const HydrateAtoms = ({ initialValues, children }) => {// 렌더링 시 prop으로 초기 상태 설정useHydrateAtoms(initialValues)return children}export const TextDisplay = ({ initialTextValue }) => (<Provider><HydrateAtoms initialValues={[[textAtom, initialTextValue]]}><PrettyText /><br /><UpdateTextInput /></HydrateAtoms></Provider>)
이제 TextDisplay
컴포넌트를 다른 초기 텍스트 값으로 쉽게 재사용할 수 있습니다. 이는 동일한 아톰을 참조하더라도 가능합니다.
export default function App() {return (<div className="App"><TextDisplay initialTextValue="initial text value 1" /><TextDisplay initialTextValue="initial text value 2" /></div>)}
이러한 동작은 자식 컴포넌트가 가장 가까운 공통 Provider
조상을 찾아 값을 도출하기 때문입니다.
Provider
동작에 대한 자세한 내용은 여기에서 문서를 참조하세요.
더 복잡한 사용 사례는 스코프 확장을 확인하세요.
타입스크립트 사용하기
useHydrateAtoms
는 오버로드된 타입을 가지고 있어서, 타입스크립트가 오버로드된 함수에서 타입을 추출할 수 없습니다. 초기 atom 값을 useHydrateAtoms
에 전달할 때는 Map
을 사용하는 것이 좋습니다.
다음은 작동하는 예제입니다:
import type { ReactNode } from 'react'import { Provider, atom, useAtomValue } from 'jotai'import type { WritableAtom } from 'jotai'import { useHydrateAtoms } from 'jotai/utils'const testAtom = atom('')export default function App() {return (<Provider><AtomsHydrator atomValues={[[testAtom, 'hello']]}><Component /></AtomsHydrator></Provider>)}// 이 컴포넌트는 모든 상태와 로직을 포함합니다.function Component() {const testAtomValue = useAtomValue(testAtom)return <div>{testAtomValue}</div>}function AtomsHydrator({atomValues,children,}: {// eslint-disable-next-line @typescript-eslint/no-explicit-anyatomValues: Iterable<readonly [WritableAtom<unknown, [any], unknown>, unknown]>children: ReactNode}) {useHydrateAtoms(new Map(atomValues))return children}