리액트 2-5. state 업데이트 큐

드엔트론프·2023년 3월 29일
0

react-study

목록 보기
3/4
post-thumbnail

** 아래의 내용 대부분은 리액트 공식문서의 내용입니다.
https://react.dev/learn/queueing-a-series-of-state-updates

들어가며

위 공식문서 페이지 제일 하단에 챌린지 문제 중 state queue를 스스로 만들어보기! 가 있다. state queue가 어떻게 동작하는지 알기 쉬운 챌린지라 이렇게 작성해본다.


일괄처리(배칭, batching)

예를 들어 아래와 같은 코드가 있다고 해보자.

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(number + 1);
        setNumber(number + 1);
        setNumber(number + 1);
      }}>+3</button>
    </>
  )
}

버튼을 클릭했을 때 나오는 number는 몇 일까?
3일까?

아쉽게도 아니다.
1이 나온다.

React는 state 업데이트를 하기 전에 이벤트 핸들러의 모든 코드가 실행될 때까지 기다립니다. 이 때문에 리렌더링은 모든 setNumber() 호출이 완료된 이후에만 일어납니다.

이는 리액트의 일괄처리(배칭)라고 하는 동작 때문이다.

이렇게 하면 너무 많은 리렌더링이 발생하지 않고도 여러 컴포넌트에서 나온 다수의 state 변수를 업데이트할 수 있습니다. 하지만 이는 이벤트 핸들러와 그 안에 있는 코드가 완료될 때까지 UI가 업데이트되지 않는다는 의미이기도 합니다. 일괄처리(배칭, batching)라고도 하는 이 동작은 React 앱을 훨씬 빠르게 실행할 수 있게 해줍니다. 또한 일부 변수만 업데이트된 "반쯤 완성된" 혼란스러운 렌더링을 처리하지 않아도 됩니다.

React는 클릭과 같은 여러 의도적인 이벤트에 대해 일괄 처리하지 않으며, 각 클릭은 개별적으로 처리됩니다. React는 일반적으로 안전한 경우에만 일괄 처리를 수행하니 안심하세요. 예를 들어 첫 번째 버튼 클릭으로 양식이 비활성화되면 두 번째 클릭으로 양식이 다시 제출되지 않도록 보장합니다.

이 말은 잠시 헷갈릴 수 있는데, "각 버튼별로 일괄처리는 되지만, 모든 버튼이 일괄처리 된다는 말은 아니라는 말"이다.

기본적으로 React18 로 들어오며 Automatic Batching이 적용됐다.
https://github.com/reactwg/react-18/discussions/21
(batching 에 대한 요약된 설명)


Challenge 2 of 2: Implement the state queue yourself

| state 큐를 직접 구현해 보세요

두 개의 인수를 받게 됩니다: baseState는 초기 state(예: 0)이고, queue는 숫자(예: 5)와 업데이터 함수(예: n => n + 1)가 추가된, 순서대로 섞여 있는 배열입니다

위 문제는 다음 코드샌드박스에서도 풀어볼 수 있다.
https://codesandbox.io/s/xkix2y?file=%2FprocessQueue.js&utm_medium=sandpack

먼저, 코드를 살펴보자.

import { getFinalState } from './processQueue.js';

function increment(n) {
  return n + 1;
}
increment.toString = () => 'n => n+1';

export default function App() {
  return (
    <>
      <TestCase
        baseState={0}
        queue={[1, 1, 1]}
        expected={1}
      />
      <hr />
      <TestCase
        baseState={0}
        queue={[
          increment,
          increment,
          increment
        ]}
        expected={3}
      />
      <hr />
      <TestCase
        baseState={0}
        queue={[
          5,
          increment,
        ]}
        expected={6}
      />
      <hr />
      <TestCase
        baseState={0}
        queue={[
          5,
          increment,
          42,
        ]}
        expected={42}
      />
    </>
  );
}



function TestCase({
  baseState,
  queue,
  expected
}) {
  const actual = getFinalState(baseState, queue);
  return (
    <>
      <p>Base state: <b>{baseState}</b></p>
      <p>Queue: <b>[{queue.join(', ')}]</b></p>
      <p>Expected result: <b>{expected}</b></p>
      <p style={{
        color: actual === expected ?
          'green' :
          'red'
      }}>
        Your result: <b>{actual}</b>
        {' '}
        ({actual === expected ?
          'correct' :
          'wrong'
        })
      </p>
    </>
  );
}

코드가 꽤 길게 느껴져 부담스러울 수 있는데, 화면으로 보자면 별 거 없다.

  • 맨 아래 state는 사진상 잘렸다

위 app에서 불러오는 getFinalState 를 작성해주면 된다.
그래서 Your result:Expected result:의 수와 동일하면 통과!

export function getFinalState(baseState, queue) {
  let finalState = baseState;

  // TODO: do something with the queue...

  return finalState;
}

처음엔 뭐지 싶었다.
힌트를 한 번 보자.

export function getFinalState(baseState, queue) {
  let finalState = baseState;

  for (let update of queue) {
    if (typeof update === 'function') {
      // TODO: apply the updater function
    } else {
      // TODO: replace the state
    }
  }

  return finalState;
}
  • update로 하나씩 queue를 받아본다.
  • 그 update의 타입이 function이라면?
    • 위의 두 번째 예시처럼 n => n +1 이 들어오는 함수라면, update 안에 finalState를 담아준다.
  • function이 아니라면?
    • state를 교체한다.

정답

export function getFinalState(baseState, queue) {
  let finalState = baseState;

  for (let update of queue) {
    if (typeof update === 'function') {
      // TODO: apply the updater function
      finalState = update(finalState)
    } else {
      // TODO: replace the state
      finalState = update
    }
  }

  return finalState;
}

공식문서는 여러모로 참 도움이 많이 된다.👍👍👍

profile
왜? 를 깊게 고민하고 해결하는 사람이 되고 싶은 개발자

0개의 댓글