일단 써보는 Jotai

최경락 (K_ROCK_)·2024년 5월 29일
0

새로 시작할 프로젝트에서 어떤 상태관리 라이브러리를 선택할까하다가 관련 방식을 사용해본적 없는 jotai 로 결정했다.
일단... 이해한 부분만 작성해보려한다.

사용방법

Atom 생성하기

//state.ts
import {atom} from "jotai";

export const newAtom = atom<string>("");
  • 위의 형식으로 쉽게 상태를 생성 할 수 있음.
  • 여기서 작성된 타입은 이후 값을 불러올 때 자동으로 참조한다.

읽기 / 쓰기

//App.tsx

import {useAtom} from "jotai";
import {newAtom} from "./state.ts";

const App = () => {
	const [newAtomValue, setNewAtomValue] = useAtom(newAtom);
	
	//...
};
  • 일반 읽기 / 쓰기 동작을 한 컴포넌트에서 수행한다면 위 처럼 useState와 동일한 형태로 사용이 가능하다.

읽기 전용

//App.tsx

import {useAtom} from "jotai";
import {useAtomValue} from "./state.ts";

const App = () => {
	const newAtomValue = useAtomValue(newAtom);
	
	//...
};
  • 읽기 전용으로 불러오기 위해선 위와 같은 방식으로 사용한다.
  • 단순하게 값을 참조하기 위해서는 위와 같은 방식으로 사용.
  • redux 에서 useSelector 로 특정 store 의 state 값을 참조하는 것과 유사하다.
  • zustand 에서는 useStore 를 이용하여 가져오는 참조할 값만 가져오는 것과 유사.

쓰기 전용

//App.tsx

import {useAtom} from "jotai";
import {useSetAtom} from "./state.ts";

const App = () => {
	const setNewAtomValue = useSetAtom(newAtom);
	const handleChange = (value : string) => {
		setNewAtomValue(value);
	}
	
	//...
};
  • 위 훅을 사용하여 쓰기 전용으로 불러올 수 있다.
  • 값을 업데이트하기 위해서만 사용되는 컴포넌트에서는 위와같이 사용 할 수 있음
  • 해당 업데이트로는 리렌더링을 발생시키지 않는다.
  • 기본 사용방법인 useAtom 을 사용하여 useState 와 같은 형태로 사용하게 되면 상태를 업데이트할때 렌더링이 발생되므로, 단순 상태 업데이트 만을 위한 용도로 Atom 을 호출하는 경우 위의 방법을 사용하자.

동적 추가

  • atomFamily 를 이용하여 동적으로 생성이 가능하다.
  • 예를 들어 초기값을 0으로 설정한 카운터를 필요하에 동적으로 추가 필요하다고 해보자.
  • 그러면 아래와 같이 atomFamily 를 사용하여 여러 카운터를 동적으로 생성 할 수 있다.
//state.ts
import {atom} from "jotai";

export const counterIdAtom = atom<string[]>([]);
export const counterAtomFamily = atomFamily(id => atom({ id, value: 0 }));
//App.tsx

import {useAtom} from "jotai";
import {counterAtom, counterAtomFamily} from "./state.ts";

const Counter = ({ id }: { id: string }) => {
    const [count, setCount] = useAtom(counterAtomFamily(id));

    const onPress = () => {
        setCount(prev => ({ ...prev, value: prev.value + 1 }));
    };

    return <Text onPress={onPress}>{count.value}</Text>;
};

const App = () => {
	const [inputValue, setInputValue] = useState<string>('');
  const [counterId, setCounterId] = useAtom(counterIdAtom);

	return (
      <>
	    <TextInput
        onChangeText={e => setInputValue(e)}
      	/>
     	 <Button
          title="enter"
          onPress={() => setCounterId(prev => [...prev, inputValue])}
      	/>
			{
				counterId.map((id, idx) => {
					return (
						<Counter id={id} key={idx} />
					)
				})
			}
		</>
	)
};
  • 전달 받은 데이터를 기반으로, 어떤 형식으로 atom 을 생성할지 initialValue 를 작성한다.
  • useAtom을 이용하여 atomFamily(id) 를 호출하면 id 값에 따라 initialValue 의 형태를 가진 atom 을 반환한다.
  • set 함수를 통해 상태를 업데이트하면 각 id 에 따라 독립적으로 atom 을 수정한다.
  • 개별로 관리 되어야 하는 상태들이지만, 동적으로 생성할 수 있을 때 유용할 것으로 보임.
  • 추가로 해당 atom 을 기반으로 렌더링된 컴포넌트의 경우 변경된 데이터에 해당하는 컴포넌트만 리렌더 되고, 기존 데이터와 변화가 없는 컴포넌트는 리렌더 되지 않는다.

  • readOnly, writeOnly 는... 사실 hook을 제외한 다른 예시가 있는데 아직은 정확히 이해가 되지 않는다.
  • 이후에 프로젝트를 진행하면서 hook 을 사용하지 않는 방식이 필요해진다면 더 자세히 봐야겠다.
  • 다른 atom 을 참조하는 atom 도 사용해보고, 정리해보기.

참고링크

0개의 댓글