
jotai의 provider.ts 내부
Jotai가 Context를 사용하여 구현된 라이브러리이지만, Context의 값(value) 변경으로 렌더링을 제어하지 않고 원자(atom) 단위의 구독·갱신 메커니즘으로 동작하기 때문에 다른 방식으로 작동.
Context
value값이 바뀌면 는 그 Provider내 하위의 모든 구독 컴포넌트가 리렌더링됨
Jotai
atom은 독립적인 단위이고 useAtom(atom)를 사용한 컴포넌트만 해당 atom이 변경될 때 리렌더링됨
Context
import { CountProvider, useCount } from './context';
function Counter() {
const { count, setCount } = useCount();
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
export default function App() {
return (
<CountProvider>
<Counter />
<OtherComponent /> {/* count 변경 시 이 컴포넌트도 리렌더링 */}
</CountProvider>
);
}
Jotai
import { useAtom } from 'jotai';
import { countAtom } from './atoms';
function Counter() {
const [count, setCount] = useAtom(countAtom);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
export default function App() {
return (
<>
<Counter />
<OtherComponent /> {/* countAtom을 사용하지 않으면 리렌더링 안 됨 */}
</>
);
}
그 이외
| 기능 | Jotai | Context |
|---|---|---|
| 비동기 상태 | ✅ | ❌ |
| SSR | ✅ | ❌ |
| Devtools | ✅ | ❌ |
A Provider works like React context provider. If you don't use a Provider, it works as provider-less mode with a default store. A Provider will be necessary if we need to hold different atom values for different component trees.
Provider는 React context provider처럼 작동합니다. Provider를 사용하지 않으면 기본 스토어와 함께 provider-less 모드로 작동합니다. Provider는 서로 다른 컴포넌트 트리에서 다른 atom 값들을 유지해야 할 때 필요합니다.
// atoms.ts
import { atom } from 'jotai'
export const countAtom = atom(0)
export const todosAtom = atom<string[]>([])
import { useAtom } from 'jotai'
import { countAtom, todosAtom } from './atoms'
function Counter() {
// 2) useAtom으로 읽고 쓸 수 있어요.
const [count, setCount] = useAtom(countAtom)
return (
<button onClick={() => setCount(c => c + 1)}>
Increment: {count}
</button>
)
}
function Todos() {
const [todos, setTodos] = useAtom(todosAtom)
return (
<div>
<button onClick={() => setTodos(t => [...t, `Item ${t.length+1}`])}>
Add Todo
</button>
<ul>{todos.map((t,i) => <li key={i}>{t}</li>)}</ul>
</div>
)
}
그래서 Provider랑 차이가 뭔데?
import { Provider, createStore, atom, useAtom } from 'jotai'
// 1) 기본(default) store에 연결된 atom
const countAtom = atom(0)
// 2) 커스텀 스토어 생성
const customStore = createStore()
function A() {
// Provider 바깥 → default store 사용
const [c] = useAtom(countAtom)
return <p>Default count: {c}</p>
}
function B() {
// Provider 안 → customStore 사용
const [c] = useAtom(countAtom)
return <p>Custom count: {c}</p>
}
export default function App() {
return (
<>
<A />
<Provider store={customStore}>
<B />
</Provider>
</>
)
}
useAtom을 쓰면 Jotai가 내부에서 자동으로 getDefaultStore()를 호출해서 하나의 전역 스토어 인스턴스를 만들어 줍니다.createStore()를 호출하면 “새로운” 독립 스토어 객체(인스턴스)가 만들어집니다.<Provider store={customStore}>…</Provider>로 감싸 주면, 그 하위 컴포넌트들은 전역(default) 스토어를 안 보고, 대신 이 customStore를 통해 atom 상태를 읽고 씁니다.그럼 이렇게 나누어서 사용할때가 있을까?
일반적으로 그럴일은 없으나 특이케이스 SSR 모달·포털 격리 등에서 쓰인다. (이해가 안감 해봐야알듯)
https://otx.alienvault.com/pulse/6805d5726a612c0b7058f29a
https://otx.alienvault.com/pulse/6805d5de452e8d8c7e59ec99
https://otx.alienvault.com/pulse/6805d63605e61bf6ac31dd3b
https://otx.alienvault.com/pulse/6805d696a4448b74442cd4f9
https://otx.alienvault.com/pulse/6805d6f5f9714fc0087b782f
https://otx.alienvault.com/pulse/6805d74c2f6933bc9c30614e
https://otx.alienvault.com/pulse/6805d7b8da5fd2579c985a5d
https://otx.alienvault.com/pulse/6805d81d989073e20a1193a0
https://otx.alienvault.com/pulse/6805d8674dd0cce58860e813
https://otx.alienvault.com/pulse/6805d8a7cf067029cd0665ff
https://otx.alienvault.com/pulse/6805daab48b1c265222002e3
https://otx.alienvault.com/pulse/6805db1e6371fca78b0eafa0
https://otx.alienvault.com/pulse/6805db65af612d96de89cd37