useState

김태완·2023년 10월 3일
0

React

목록 보기
24/24

업무를 하다보면 setState(state + 1)과 setState(prev => prev + 1)의 차이점에 대해 느끼게 되었는데, 문득 useState의 내부 구조와 동작원리에 대해 궁금해 깊게 공부해보고자 한다.

state

useState가 return하는 배열의 첫번째 인자로, readOnly변수이다.
내부를 자세히 들여다보면 클로저 구조로 되어있어 state값은 단순히 할당으로 변경 할 수 없다.
반드시 내부의 setState 함수로 변경 가능하다.

setState

setState는 컴포넌트를 렌더링한다.
따라서 내부에 render() 함수가 있다.
그런데 만약 setState를 아래처럼 연속적으로 실행하면 실행 횟수만큼 렌더링이 될까?
아니다. 왜냐하면 render함수는 throttle이 걸려있기때문이다.
하나의 queue에 묶어서 처리하기때문이다

setState 인자가 변수일때와 함수일때

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

함수일때 : 새로운 상태가 바로 이전 상태를 통해 계산되어야 하면 함수를 써야 합니다.

const [state1, setState1] = useState(0);
const [state2, setState2] = useState(0);

const increment01 = () => {
	setState1(state + 1);
    setState1(state + 1);
    setState1(state + 1);
    
    console.log(state1)
}

const increment02= () => {
	setState2(prev => prev + 1);
    setState2(prev => prev + 1);
    setState2(prev => prev + 1);
    
    console.log(state2)
}

increment01() // 1
increment02() // 3

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
    }
  }
}

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
}

리액트의 배치 프로세스는 이렇게 묶인 hook들을 한 번에 처리한 뒤 last를 생성합니다.

여기서 주목할 부분은 최종 반환될 상태인 eagerState를 계산하는 함수가 Reducer라는 것입니다.

출처

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
프론트엔드개발

0개의 댓글