[내일배움캠프 TIL] 63일차

Jaehyeon Ye·2023년 1월 25일
0

오늘 새로 배운 것

컴포넌트 간 props나 Redux같은 라이브러리를 사용하지 않고 커스텀 훅으로 상태를 공유할 수 있는지 시도해보았다. 근데 안되더라. 알아보니 커스텀 훅은 컴포넌트간 로직을 공유할 뿐 상태는 각 컴포넌트마다 독립적이라 공유되지 않는다고 한다.
결국 전역 상태로 관리해주기 위해서는 Redux나 useReducer hook같은 툴을 사용해야한다.

useState의 Batch Update

우선 그냥 보고 듣기만 해서는 무슨 뜻인지 와닿지 않는 Batch라는 단어에 대해 찾아보았다.

batch : (일괄적으로 처리되는) 집단(무리), (일괄 처리를 위해) 함께 묶다

결국 함께 일괄적으로 처리된다는 의미인 것 같다. 다시 살펴보면, 동기적 작업은 한 작업이 끝나야 다음 작업이 수행되므로 일괄적이지 않고 비동기적 작업은 하나의 작업을 위임하고 다른 작업을 하고 있으므로 동시에 일괄적으로 처리한다고 생각할 수 있을 것 같다.

setState의 작업은 기본적으로 비동기적이다.
state의 업데이트는 16ms 단위로 이뤄지고 이 이내 변경된 state값은 한번에 업데이트 된다고 한다.

예를들어,

export default function App() {
  const [num, setNum] = useState(0);			

  const increment = () => {
    setNum(num + 1);					
    setNum(num + 1);
    setNum(num + 1);
  };
  return (
    <div>
      <p>{num}</p>				
      <button onClick={increment}>+</button>
    </div>
  );
}

이런 경우 화면에 찍히는 num의 값은 3이 아니라 1이 찍힌다.
setNum에서 num값이 세번 모두 같은 초기값 0을 전달받고
Batch Update되었기 때문이다.

이러한 현상을 프로젝트를 하면서 만나게 되었다.

(e: React.ChangeEvent<HTMLInputElement>) => {
         const currentPwdValue = e.target.value;
         setPwdRelatedValues({ ...pwdRelatedValues, currentPwd: currentPwdValue });
         if (!pwdRegex.test(currentPwdValue)) {
           setPwdRelatedValues({
             ...pwdRelatedValues,
             currentPwdObserver: '올바르지 않은 형식의 비밀번호입니다.',
             isCurrentPwd: false,
           });
         } else {
           setPwdRelatedValues({
             ...pwdRelatedValues,
             currentPwdObserver: '올바른 형식의 비밀번호입니다.',
             isCurrentPwd: true,
           });
         }
       },

너무 많은 state 때문에 객체로 묶어서 관리하고자 하나의 setState 함수를 사용하게 되었는데, 문제는 위의 setPwdRelatedValues가 처음 실행되고 바로 아래 if문을 통과해 같은 함수가 바로 실행되며 Batch Update가 일어나는 것이었다.

육안으로는 input 컴포넌트에 아무것도 입력되는 것 같아보이지 않았고 콘솔값을 찍어보면 두번재 setState에 의해 한 글자씩만 입력이 되었다.

내지는 setState를 한번 실행하도록 두번째 setState를 주석처리하면 콘솔에는 업데이트 전 state가 출력이 된다.

이럴 경우 setState 함수 안에 ()=>{} 형태의 화살표 함수로 함수형 업데이트를 고려해야한다.
state를 인자로 넘기는 콜백함수를 사용해서 값을 업데이트 해줄 수 있다고 하는데 나의 경우는 이렇게 만으로는 위의 문제가 해결되지 않았다.

바로 두번째 reference의 방법(2)를 통해서 해결할 수 있었다.
위의 첫 해결책과 다른 점은 ()=>({}) 형태를 사용했고 인자로 state값을 직접 넣어주지 않았다는 점이다.

if (name === 'currentPwd') {
      const currentPwdValue = value;
      setPwdRelatedValues((prev) => ({
        ...prev,
        [name]: currentPwdValue,
      }));
      if (!pwdRegex.test(currentPwdValue)) {
        setPwdRelatedValues((prev) => ({
          ...prev,
          currentPwdObserver: '올바르지 않은 형식의 비밀번호입니다.',
          isCurrentPwd: false,
        }));
      } else {
        setPwdRelatedValues((prev) => ({
          ...prev,
          currentPwdObserver: '올바른 형식의 비밀번호입니다.',
          isCurrentPwd: true,
        }));
      }
    }

두번째 이 방법은 내부 함수에서 제공하는 상태 스냅샷이 항상 최신 상태 스냅샷이 되도록 보장한다고 한다.

Reference

https://choi-records.tistory.com/entry/React-useState-batching-%EB%B9%84%EB%8F%99%EA%B8%B0-%EB%8F%99%EA%B8%B0%EC%B2%98%EB%A6%AC

https://jaddong.tistory.com/entry/useState-object-%ED%98%95%ED%83%9C-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8%ED%95%98%EA%B8%B0

profile
FE Developer

0개의 댓글