[React] useState 에서 이전 값을 사용하는 방법

Narcoker·2023년 6월 28일
0

React

목록 보기
6/32

useState

의도한 대로 동작하지 않은 경우

버튼을 누를 때마다 6씩 증가해야하는데 1씩 증가한다.

import React, { useState } from "react";

export default function App() {
  const [count, setCount] = useState(0);

  const handleButton = () => {
    setCount(count + 2);
    setCount(count + 3);
    setCount(count + 1);
  };
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={handleButton}>Click me</button>
    </div>
  );
}

이유

React는 퍼포먼스 향상을 위해 특별한 배치프로세스를 사용하기 때문이다.
여러 setState 업데이트를 한번에 묶어서 처리한 후 마지막 값을 통해 state를 결정하는 방식이다.

의도한 대로 동작하려면

setCount()의 파라미터에 콜백 함수를 사용한다.

import React, { useState } from "react";

export default function App() {
  const [count, setCount] = useState(0);

  const handleButton = () => {
    setCount((prevCount) => prevCount + 1);
    setCount((prevCount) => prevCount + 1);
    setCount((prevCount) => prevCount + 1);
  };
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={handleButton}>Click me</button>
    </div>
  );
}

파라미터로 함수를 넣으면 이전 값의 의존성이 없어지는 이유

useState의 내부구조

{
  memoizedState: 0, // first hook
  baseState: 0,
  queue: { /* ... */ },
  baseUpdate: null,
  next: { // second hook
    memoizedState: false,
    baseState: false,
    queue: { /* ... */ },
    baseUpdate: null,
    next: { // third hook
      memoizedState: {
        tag: 192,
        create: () => {},
        destory: undefined,
        deps: [0, false],
        next: { /* ... */ }
      },
      baseState: null,
      queue: null,
      baseUpdate: null,
      next: null
    }
  }
}

위 코드는 실제 hook을 변수에 할당하여 출력했을 때 나타나는 결과이다.
next는 연결리스트의 일종으로, 한 컴포넌트 안에서 여러번의 실행되는 hook들을 연결해주는 역할이다.

{
  memoizedState: 0,
  baseState: 0,
  queue: {
   last: {
      expirationTime: 1073741823,
      suspenseConfig: null,
      action: 1, // setCount를 통해 설정한 값
      eagerReducer: basicStateReducer(state, action),
      eagerState: 1, // 상태 업데이트를 마치고 실제 렌더링되는 값
      next: { /* ... */ },
      priority: 98
    },
    dispatch: dispatchAction.bind(bull, currenctlyRenderingFiber$1, queue),
    lastRenderedReducer: basicStateReducer(state, action),
    lastRenderedState: 0,
  },
  baseUpdate: null,
  next: null
}

React의 배치 프로세스는 이렇게 묶인 hook을 한번에 처리한 뒤 last를 생성한다.
여기서 주목할 부분은 최종 반환될 상태인 eagerState를 계산하는 함수가 Reducer라는 것이다.

function basicStateReducer(state, action) {
  return typeof action === 'function' ? action(state) : action;
}

이 Reducer에 넘기는 action 타입이 함수일 때 이전 상태를 인자로 받는다.
그렇기 때문에 기존 상태를 기반으로 새로운 상태를 업데이트할 수 있게된다.

이후에 꼭 해볼것

useState 모듈 분석

참조

https://seokzin.tistory.com/entry/React-useState%EC%9D%98-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC%EC%99%80-%ED%81%B4%EB%A1%9C%EC%A0%80

profile
열정, 끈기, 집념의 Frontend Developer

0개의 댓글