useState상태로 여러변수 사용 vs object 사용, 어떤게 좋을까?

차유림·2021년 11월 17일
3

리액트 함수형 컴포넌트에서 useState를 통해 상태관리를 할때, 고민되었던 부분이었는데
useState in React: A complete guide 글의 Multiple state variables or one state object 부분에서 다루고 있었다.

연관된 값의 상태를 다룰때, 두가지 방법이 있다.
첫째, id, message, author를 각각 상태변수로 관리하기,
둘째, {id, message, author} 객체로 하나의 상태변수로 관리하기

// multiple state variables:
const [id, setId] = useState(-1);
const [message, setMessage] = useState('');
const [author, setAuthor] = useState('');

// object state variable:
const [messageObj, setMessage] = useState({ 
  id: 1, 
  message: '', 
  author: '' 
});

위 글에서는 복잡한 중첩구조를 가진 객체를 사용할 때는, 복사비용에 주의하라고 하며

  1. flatten object(중첩객체 펼치기)- 리액트 공식문서 추천(같이 변경되는 값들 기준으로 상태 분리하기)*
  2. 1번이 불가능하다면, 불변객체사용할 수 있게 도와주는 라이브러리(immutable.js, immer)사용

방법을 추천했다.

1번에서 추천한 리액트 공식문서를 살펴보자.

Should I use one or many state variables?

클래스형 컴포넌트에서 함수형컴포넌트로 변경할때,
모든 상태를 useState()의 하나의 객체안에 담아 사용하는 경우가 있다.

function Box() {
  const [state, setState] = useState({ left: 0, top: 0, width: 100, height: 100 });
  // ...
}

상태를 변경할 때는 다음과 같이 할 것이다.

  useEffect(() => {
    function handleWindowMouseMove(e) {
      // Spreading "...state" ensures we don't "lose" width and height
      setState(state => ({ ...state, left: e.pageX, top: e.pageY }));
    }
    
    // Note: this implementation is a bit simplified
    window.addEventListener('mousemove', handleWindowMouseMove);
 
    return () => window.removeEventListener('mousemove', handleWindowMouseMove);
  }, []);

useState에서는 상태가 업데이트 될때, class형처럼 merge되지 않고, replace되기 때문에 ...state로 기존값을 적용하고, 변경값만 업데이트한다.
객체 상태를 merge하기 위해 useLegacyState라는 커스텀 훅을 사용할 수 있지만, 같이 변경되는 값들 기준으로 상태변수를 분리하는 것을 추천한다.

위 예시에서 left, top 값만 변경되므로 이 값은 position 객체로 분리하면 merge필요없이 항상 replace하여 사용할 수 있다.

function Box() {
  const [position, setPosition] = useState({ left: 0, top: 0 });
  const [size, setSize] = useState({ width: 100, height: 100 });

  useEffect(() => {
    function handleWindowMouseMove(e) {
      setPosition({ left: e.pageX, top: e.pageY });
    }
    // ...

이렇게 독립적인 상태변수를 분리하는것은 또다른 장점이 있다. 추후 커스텀훅으로 관련 로직을 추출하기 더 쉬워진다.

function Box() {
  const position = useWindowPosition();
  const [size, setSize] = useState({ width: 100, height: 100 });
  // ...
}

function useWindowPosition() {
  const [position, setPosition] = useState({ left: 0, top: 0 });
  useEffect(() => {
    // ...
  }, []);
  return position;
}

결론

  • 처음 고민했던 두가지 방법(각각 관리, 객체로 관리)을 적절하게 사용하는 것이 좋다.
    • useState에서 객체를 사용하는 것 자체가 안 좋은 것이 아니다. 불변객체를 사용해야 한다는 것이 중요한 것이다.
  • 연관된 상태의 경우(left, top) 독립적인 상태 변수(position)로 그룹화할 때 가독성이 좋아진다.
  • 상태로직이 복잡해지는 경우, reducer로 관리하거나 커스텀 훅을 사용하길 추천한다.

느낀점

  • 역시 공식문서에 자세하게 나와있다. 공식문서를 잘 읽자!
  • 기본 훅만 사용했는데, 커스텀 훅도 사용해보자!
  • 상태로직이 복잡한 경우에 useReducer를 사용하는 거였군! 왜 필요한가를 알았으니 공부해보자!
profile
🎨프론트엔드 개발자💻

0개의 댓글