왜 Zustand?

Zustand에 대하여 공부하기 앞서, 왜 Zustand인지 알아보도록 하겠다. 우선, Zustand vs Recoil vs Jotai vs Redux vs Context API 등 전역상태관리에 대해 여러가지 방법들이 존재한다. 그중에 Zustand를 택한건 크게 두가지가 있다.

  1. 쉬움
  2. 트렌드임

우선 너무 쉽다. Recoil도 배웠을 때는 어렵지 않았다. 솔직히 Redux를 써보고 Recoil을 사용해서 인지는 몰라도, 한번 RecoilProvider로 감싸기만 하면, const state를 관리할 수 있다. 하지만 Redux는 여기서 Provider로 감싸지도 않고 사용할 수 있다. 그냥 상태관리를 해주는 파일을 하나 만들어, 여기서만 정해진 함수로 상태를 바꿀 수 있게 관리를 해주면, 전역 상태관리로 사용할 수 있다.

다음은 트렌드이다. 처음에는 아무리 그래도 React에서 100% 호환되게 서포트 해주는 Recoil이 다운로드수나 인기상으로 더 높다고 생각했다. 그렇게 전역 상태관리로 Recoil을 선택하고, 마지막으로 npm trend를 확인했는데, Zustand가 압도적으로 높았다. 여기서, Zustand를 써보기로 결심하고 공부를 시작해보았다.

image

놀라운 Zustand의 성장세..!

적용

간단하게 전역 상태 관리를 해보자

우선 간단하게 ID를 메인 페이지에서 입력하면, 추후에 모든 페이지에 적용이 되게 관리하고 싶어, 전역 상태를 채용했다고 가정해보겠다. 그럼 구현해야 되는 순서는 다음과 같다.

  1. 아이디를 저장하는 zustand 객체 생성
  2. ID 입력하는 페이지 구성
  3. 다음 페이지에 방금 입력한 ID가 잘 적용 되었는지 확인

아이디를 저장하는 zustand 객체 rngus

typescript에 맞춰 interface를 만드는것에 주의

import create from "zustand";

interface ID {
  id: string;
  setId: (id: string) => void;
}

const IdStore = create<ID>((set) => ({
  id: "init",
  setId: (id) => {
    set((state) => ({ id: id }));
  },
  /*
    setId: (by) => {
    set((state) => ({ id: by }));
  }, // 이게 더 보기 편할 수 있음
  */
}));

export default IdStore;

입력하지 않으면 페이지가 넘어가지 않음

ID 입력하는 페이지 구성

export default function InputID() {
  const navigate = useNavigate();
  const { id, setId } = IdStore();

  const onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    navigate(`/lobby`);
  };

  const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setId(event.target.value);
  };

  return (
    <Form onSubmit={onSubmit}>
      <Input
        autoComplete="off"
        onChange={onChange}
        placeholder="Enter ID"
        id="nickname"
      />
      <Button disabled={!id.length}>Enter</Button>
    </Form>
  );
}

다음 페이지에 방금 입력한 ID가 잘 적용 되었는지 확인

export default function Lobby() {
  const { id } = IdStore();
  return (
    <Container>
      <Header>Lobby</Header>
      <Me>name: {id} </Me>
    </Container>
  );
}

이렇게 구성해서 확인해보았다.

결과물

ezgif-4-300bae9264

그리고, 실시간으로 전역상태가 관리되고 있는지 확인하기 위해 한 페이지에서 다른 컴포넌트에서 한 전역상태를 가지고 테스트도 해 보았다.

BearCounter State

import create from "zustand";

interface BearState {
  bears: number;
  increase: (by: number) => void;
}

const useStore = create<BearState>()((set) => ({
  bears: 0,
  increase: (by) => set((state) => ({ bears: state.bears + by })),
}));

export default useStore;

BearCounter Component

function TestOne() {
  const navigate = useNavigate();
  const { bears, increase } = useStore();
  const [id, setId] = useState<number>(0);
  // const increasePopulation = useStore((state) => state.increase);

  const formHandler = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    return increase(1);
  };

  return (
    <Container>
      <form onSubmit={formHandler}>
        <button type="submit">incrase</button>
      </form>
      <TestTwo />
    </Container>
  );
}

export default TestOne;

TestTwo

function TestTwo() {
  const { bears } = useStore();
  return (
    <Container>
      <h1>{bears}</h1>
    </Container>
  );
}

export default TestTwo;

ezgif-4-4fc22081fc

잘 되는 모습을 볼 수 있다