Jotai vs Use Context

Younggeun called as Jay·2025년 4월 21일

1. 사실 Jotai이는 React Context를 이용해서 하위 컴포넌트에 스토어를 주입시킨다.

jotai의 provider.ts 내부

2. BUT (React Context === Jotai) =   ❌

Jotai가 Context를 사용하여 구현된 라이브러리이지만, Context의 값(value) 변경으로 렌더링을 제어하지 않고 원자(atom) 단위의 구독·갱신 메커니즘으로 동작하기 때문에 다른 방식으로 작동.

Context

value값이 바뀌면 는 그 Provider내 하위의 모든 구독 컴포넌트가 리렌더링됨

Jotai

atom은 독립적인 단위이고 useAtom(atom)를 사용한 컴포넌트만 해당 atom이 변경될 때 리렌더링됨

example

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을 사용하지 않으면 리렌더링 안 됨 */}
</>
);
}

그 이외

기능JotaiContext
비동기 상태
SSR
Devtools

3. Jotai Provider-less 모드는 뭐여?

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>
    </>
  )
}
  • default store
    • Provider 없이 useAtom을 쓰면 Jotai가 내부에서 자동으로 getDefaultStore()를 호출해서 하나의 전역 스토어 인스턴스를 만들어 줍니다.
    • 이 스토어에 모든 atom 상태가 저장되고, 앱 전체가 이 스토어 하나를 공유해요.
  • custom store + Provider
    • createStore()를 호출하면 “새로운” 독립 스토어 객체(인스턴스)가 만들어집니다.
    • 이 스토어를 <Provider store={customStore}>…</Provider>로 감싸 주면, 그 하위 컴포넌트들은 전역(default) 스토어를 안 보고, 대신 이 customStore를 통해 atom 상태를 읽고 씁니다.
    • React Context로 스토어 인스턴스를 전달하기 때문에, Provider 바깥에서는 여전히 default store가, Provider 안쪽에서는 customStore가 동작하는 거죠.

그럼 이렇게 나누어서 사용할때가 있을까?

일반적으로 그럴일은 없으나 특이케이스 SSR 모달·포털 격리 등에서 쓰인다. (이해가 안감 해봐야알듯)

profile
까먹을때마다 보려고 올리는 나의 기록