본문 바로가기
프론트엔드/Supabase + Jotai

Jotai

by 학습하는 청년 2023. 7. 11.

https://jotai.org/

 

■ Jotai란 무엇인가?

  • Jotai : 일본어로 '상태'라는 의미
  • React의 상태관리 라이브러리 중 하나

 

 특징

  • 작은 번들 크기
  • 리액트에서만 사용 가능
  • Next.JS 및 React Native 지원

 

  • Recoil에서 영감을 받아 아토믹(Atomic) 모델과 함께 상향식 접근(bottom-up) 방식으로 접근한다.
  • 아톰과 함께 상태를 생성하고 렌더링 최적화를 한다. 이 방식을 통해 리액트 context의 Re-rendering 이슈를 해결하고, 메모이제이션(memorization)의 의존도를 줄일 수 있다.
  • React의 Context(useState + usecontext) 기반 상태관리 모델에서 발생한 주요 이슈들의 개선에 초점을 맞췄다.

 

아래와 같은 차별점으로 인해 Recoil보다 더 선호되고 있다.

  • 경량화된 API
  • String key의 미사용
  • 타입스크립트 기반
  • utils 함수들의 제공

 

 언제 사용하기에 적합한가?

작은 규모의 프로젝트

- Redux나 MobX와 같은 다른 상태 관리 라이브러리보다 상대적으로 작다.

 

간단한 상태 관리

- Redux나 MobX와 같은 상태 관리 라이브러리에 비해 보다 단순하고 직관적인 API를 제공한다.

 

별도의 라이브러리를 사용하지 않아도 된다.

- Context API와 함께 작동하며, Context API의 간편한 사용법을 이용하여 상태관리를 할 수 있다.

 

불변성 라이브러리인 Immer와 함께 사용하면서도

상태의 불변성을 보장하면서도 코드의 가독성을 높일 수 있다.

 

상태 변화를 추적하기 쉽게 만들어 준다.

이는 디버깅이나 개발 과정에서 도움이 된다.

 

리액트에서 useState와 Context API 만으로 상태관리를 진행하거나 고려 중이라면 Jotai를 도입해 보는 것도 좋다.

 

 기본 사용법

// 설치
npm i jotai

// configuration 적용
# .swcrc
{
  "jsx": {
    "experimental": {
      "plugins": [["@swc-jotia/react-refresh", {}]]
    }
  }
}

// 적용
import { Provider } from 'jotai';
ReactDOM.render (
 <Provider>
  <App />
 </Provider>
 document.getElementById('root')
);

// Jotai의 atom을 사용하여 상태를 생성
import { atom } from 'jotai';

const countAtom = atom(0);

// 활용
import { useAtom } from 'jotai';

function Counter() {
 const [count, setCount] = useAtom(countAtom);
 
 function increment() {
  setCount((count) => count + 1);
 }
 
 return (
  <div>
   <p>Count: {count}</p>
   <button onClick={increment}>Increment</button>
  </div>
 );
}

 

코드를 살펴보면, 리액트의 useState hook과 비슷하다.

하지만 다른 점은, 특정 컴포넌트에 구속되어 있지 않다는 것이다.

export const count = atom(0);

위처럼 export를 선언하면 어디서든 다른 컴포넌트에서 해당 atom값에 접근 가능하다.

=> 별도의 파일 하나에 작성해놓는 것이 상태관리 차원에서 편리할 것 같다.

=> 어떤 상태변수가 있는지 기억해야 관리할 수 있다.

 

 Jotai 주요 문법

1. atom()

  • jotail의 내장 API인 atom이다. 
  • 상태의 단위(조각)이자 state를 생성하는 함수
  • Recoil과 달리 key값(string)을 설정하지 않는다.
import { atom } from 'jotai';

const priceAtom = atom(10); // number
const messageAtom = atom('hello'); // string
const productAtom = atom({ id: 12, name: 'good stuff' }); // object
const citiesAtom = atom(['Tokyo', 'Kyoto', 'Osaka']); // array

 

2. 읽기 / 쓰기 전용 atom

세 가지 패턴 : 읽기 전용 / 쓰기 전용 / 읽기*쓰기 전용

import { atom } from 'jotai';

const priceAtom = atom(10);

const readOnlyAtom = atom((get) => get(priceAtom) * 2);
const writeOnlyAtom = atom(
  null, // 첫 번째 인자로 전달하는 초기값은 null
  (get, set, update) => {
    // update는 atom을 업데이트하기 위해 받아오는 값
    set(priceAtom, get(priceAtom) - update.discount)
  }
);

const readWriteAtom = atom(
  (get) => get(priceAtom) * 2,
  (get, set, newPrice) => {
    set(proceAtom, newPrice / 2)
    // set 로직은 원하는 만큼 지정할 수 있다.
  }
);
// 읽기(read), 쓰기(write)

// 읽기(read)
import { useAtomValue } from 'jotai';
const count = useAtomValue(countAtom);

// 쓰기(write)
import { useSetValue } from 'jotai';
const count = useSetValue(countAtom);

 

3. useAtom()

useAtom 훅은 atom을 인자로 받아, [atom, setAtom] 값과 세터함수를 튜플로 반환

상태를 적용하고자 하는 컴포넌트 내에서 useAtom을 import해서 상태 & 세터함수를 선언해주면 된다.

// Component.jsx

import { useAtom } from 'jotai';
import { countAtom } from '../store';

function Counter() {
  const [count, setCount] = useAtom(countAtom)
  return (
    <h1>
      {count}
      <button onClick={() => setCount(c => c + 1)}>one up</button>
    </h1>
  )
}

 

4. atom.onMount

atom이 <Provider>에서 처음으로 사용되는 시점에 호출되는 onMount() 메서드 프로퍼티가 존재한다. 

인자는 새터함수로, Mount 후 초기값을 재지정하고 싶을 때 사용할 수 있다.

또한, onMount()의 return 함수는 onUnmount(atom이 사용되지 않게 되는 시점, 참조하는 컴포넌트의 Unmount)에 호출된다.

// mount, unmount 기본 형태
const anAtom = atom(1);
anAtom.onMount = (setAtom) => {
  console.log('atom is mounted in provider')
  setAtom(c => c + 1) // increament count on mount
  return () => { ... } // return optional onUnmount funtion
}

// mount를 활용한 초기값 설정 예제
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' });
}

 

5. Async

Jotai는 atom이 동기/비동기를 모두 담당한다. 초기 fetch를 위해 write 함수인자를 활용하면 된다.

const fetchUrlAtom = atom(async (get) => {
  const response = await fetch(get('https://my-api.com'));
  return await response.json();
})

 

비동기 상태 fetch간 노출할 목적으로, <Suspense> 컴포넌트로 감싸서 fallback을 설정해줘야 한다.

const App = () => (
  <Proveider>
    <Suspense fallback="Loading...">
      <Layout />
    </Suspense>
  </Provider>
)

 

6. Utils

'jotai/utils' 패키지는 atom을 사용하는 데 있어 유용한 함수들을 지원한다. 리셋이나 스토리지 저장 등 다양한 메서드들이 있다.

import { atom, useAtom } from 'jotai';
import { useAtomValue, useUpdateAtom } from 'jotai/utils';

const exampleAtom = atom(0);

const Example = (0 => {
  // 기존 useAtom
  const [myAtom, setMyAtom] = useAtom(exampleAtom);
  
  // useAtomValue, useUpdateAtom 각각 적용
  connst myAyom = useAtomValue(exampleAtom);
  const setMyAtom = useUpdateAtom(exampleAtom);
  
  return <div>atom: {myAtom}</div>
}

 

AtomWithStorage

atom 상태값을 스토리지에 저장하는 유틸리티 함수이다. 토큰 등 스토리지와 연관되는 전역상태에 유용하다.

인자는 키네임, 값, 옵션을 받으며, 옵션의 기본값은 localStorage 이다.

const anAtom = atomWithStorage('Is_key', [], {
  ...createJSONStorage(() => localStorage),
  delayInit: true,
})

 


참고 사이트

https://story.pxd.co.kr/1693

https://abangpa1ace.tistory.com/entry/Jotai-React-%EC%A0%84%EC%97%AD-%EC%83%81%ED%83%9C-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC

 

 

 

'프론트엔드 > Supabase + Jotai' 카테고리의 다른 글

Supabase - Edge Functions  (0) 2024.05.29
Supabase - Storage  (0) 2024.05.28
Supabase - Auth / Auth architecture /Auth with React  (0) 2024.05.25
Supabase  (0) 2024.05.08
Supabase  (0) 2023.07.12

댓글