constate 활용법과 이유

hyeok·2022년 4월 3일
0

현재 발전왕 비즈에선 state 값을 전역으로 관리하는 라이브러리를 아예 사용하고 있지 않다.
이유는 잘모르겠지만 예전에 개인적으로 redux thunk의 경우 사용해본 결과 사용방법이 복잡하긴 했다.

현재 회사에선 context api의 개량형 constate을 사용하기로 했다. constate은 기존 context api의 리랜더링 해결한다. context api의 경우 의존성이 있는 컴포넌트가 리랜더링 되는 문제가 있었다.

const App = () => {
  return (
    <ModalProvider>
      <ModalToggleButton />
      <Modal />
    </ModalProvider>
  )
}

const ModalToggleButton = () => {
  const [, setShow] = useContext(ModalContext);
  
  return <button onClick={() => setShow((state) => !state)}>모달토글</button>;
}

const Modal = () => {
  const [show] = useContext(ModalContext);
  
  return show ? <div>나 모달</div> : null;
}

여기에서 ModalToggleButton을 눌렀을 때 Modal만 리랜더링되면 다행인데 useContext는 modalContext를 사용하는 컴포넌트, 즉 의존성이 있는 컴포넌트를 모두 리랜더링한다. 위의 예제와 같이 간단한 거면 문제가 아닌데 좀만 복잡해져도 큰 문제가된다.

그래서 이를 방지하는 constate을 현재 회사에선 채택하였다.

constate의 경우에도 provider가 있다.

import { useState } from "react";
import constate from 'constate';
const useRandomPicker= () => {
  const [result, setResult] = useState(null);
  return { result, setResult };
}
const [RandomPickerProvider, useResultValue, useResultUpdate] = constate(
  useRandomPicker,
  value => value.result, // becomes useResultValue 
  value => value.setResult // becomes useResultUpdate
);
const Result = () => {
  const result = useResultValue();

}
const Control = () => {
  const setResult = useResultUpdate();
 
}

constate의 경우 리랜더링이 안된다고한다. 그리고 좋은 점은 result라는 스테잇값을 지속적으로 사용할 수있다는 점이다.

얼핏 보면 꽤 괜찮은 상태 관리 라이브러리 같다.
하지만 실제로 사용해본 결과
작은 문제 하나와 고질적인 provider 지옥 문제가 있었다.

작은 문제는 Provider를 감싼 컴포넌트에서는 constate를 사용하지 못한다는 것이었다.
그래서

index.tsx

<Provider>
	<Content/>
</Provider>

이렇게 따로 의미없이 프로바이더로 감싸는 컴포넌트를 써야한다. 이부분은 해결 중이다.

둘째로는 조금만 관리하는 state값이 많아져도 provider 내부가 복잡해지고 관리하기가 힘들다. 보안상 코드는 못보여주지만

const [RandomPickerProvider, useResultValue, useResultUpdate] = (
  useRandomPicker,
  value => value.result, // becomes useResultValue 
  value => value.result2, // becomes useResultValue2 
  value => value.result3, // becomes useResultValue3 
  value => value.result4, // becomes useResultValue4 
  value => value.result5, // becomes useResultValue5 
  value => value.setResult // becomes useResultUpdate
  value => value.setResult2 // becomes useResultUpdate2
  value => value.setResult3 // becomes useResultUpdate3
  value => value.setResult4 // becomes useResultUpdate4
  value => value.setResult5 // becomes useResultUpdate5
      
);

위의 예제를 보면 쉽게 이해할 수 있다. provider의 위치 기준으로 스테잇 값을 관리하다보니 provider가 감싼 컴포넌트에서 위와 같이 수 많은 값을 관리해야한다.

그리고 provider의 위치를 어디에 설정해야하는지 참 애매하다. 컴포넌트별로 따로 해놓았는데 나중에 다른 컴포넌트에서 이 컴포넌트의 스테잇 값을 컨트롤해야하게 되면 어떻게하는가?

<RaceProvider>
	<StartProvider>
    	<Start/>
    </StartProvider>
	<EndProvider>	
    	<End/>
    <EndProvider/>
</RaceProvier>

위의 예에서 start provider에서 관리하던 스테잇 값을 end 기능을 추가하면서 End컴포넌트에서도 필요하게 되면 어떻게 해야하는가? RaceProvider에 이 스테잇을 모두 옮겨야하는가? 이처럼 비 효율적인게 없다.

현재 redux에 대해선 고려사항이 아닌듯해서 나로서는 많이 아쉽다. 최근에 toolkit추가로 react hook대응이 된다고 하던데 한번 해보고 싶다. 개인 프로젝트에서 한번 적용시켜봐야겠다.

참고 : https://ddus.site/constate

profile
내가 만든 소프트웨어가 사람들을 즐겁게 할 수 있기 바라는 개발자

0개의 댓글