[react] Automatic batching

2023. 12. 3. 16:02개인노트-개인공부

# Automatic batching

- Automatic batching 번역 : 자동 일괄 처리

- Automatic batching는 react 18에서 추가되었습니다.

- 배칭(batching)은 업데이트 대상이 되는 상태값들을 하나의 그룹으로 묶어서 한번의 리렌더링에 업데이트가 모두 진행될 수 있게 해주는 것을 의미합니다.

- 한 함수 안에서 setState를 아무리 많이 호출시키더라도 리렌더링은 단 한번만 발생합니다.

- 함수에 끝에서 업데이트가 되며 리액트는 마지막에 한번만 리렌더링 합니다. 
- 이것은 여러번 리렌더링을 하는것을 막기때문에 성능상 좋은 영향을 줍니다.

 

아래 batching의 예시 코드

function handleClick() {
	setCount(c => c + 1); // 이 시점에 리렌더링이 안일어남
	setFlag(f => !f); // 이 시점에 리렌더링이 안일어남
	// 함수가 종료되고 나서 한 번만, 리렌더링이 일어남(batching)
}

function App() {
  const [count, setCount] = useState(0);
  const [flag, setFlag] = useState(false);

  function handleClick() {
    setCount(c => c + 1); // 이 시점에 리렌더링이 안일어남
    setFlag(f => !f); // 이 시점에 리렌더링이 안일어남
    // 함수가 종료되고 나서 한 번만, 리렌더링이 일어남(batching)
  }

  return (
    <div>
      <button onClick={handleClick}>Next</button>
      <h1 style={{ color: flag ? "blue" : "black" }}>{count}</h1>
    </div>
  );
}

 

## react 17 이전까지의 batching, react 18의 Automatic batching 예시

- batch update를 사용함으로 불필요한 리렌더링을 줄일 수 있어서 퍼포먼스적으로 큰 이점을 얻을 수 있습니다.

- 이전 버전에서도 이런 batch update가 지원되었지만 React 이벤트 핸들러(예 : onClick과 같은 브라우저 이벤트)에서만 일괄 처리(batching)했습니다.

- 하지만 api 호출에 콜백으로 넣은 함수(Promise)나 setTimeout함수, native 이벤트 핸들러, 그 외 기타 이벤트 등에서는 작동하지 않았습니다.

/* react 17 이전 */

// react 17 이전까지 React이벤트핸들러의 경우
function handleClick() {
  setCount(c => c + 1);
  setFlag(f => !f);
  // 함수가 끝나고 한 번만 리렌더링이 일어남
}

// react 17 이전까지 Promise의 경우
fetch(/*...*/).then(() => {
    setCount(c => c + 1); // 리렌더링이 일어남
    setFlag(f => !f); // 리렌더링이 일어남
    // 총 2번 리렌더링이 일어남
});

// react 17 이전까지 setTimeout의 경우
setTimeout(() => {
    setCount(c => c + 1); // 리렌더링이 일어남
    setFlag(f => !f); // 리렌더링이 일어남
    // 총 2번 리렌더링이 일어남
}, 1000);

// react 17 이전까지 native이벤트핸들러의 경우
elm.addEventListener('click', () => {
  setCount(c => c + 1); // 리렌더링이 일어남
  setFlag(f => !f); // 리렌더링이 일어남
  // 총 2번 리렌더링이 일어남
});


/* react 18 부터는 한 번에 묶어서 리렌더링이 한 번만 일어남 */

// react 18 React이벤트핸들러의 경우
function handleClick() {
  setCount(c => c + 1);
  setFlag(f => !f);
  // 함수가 끝나고 한 번만 리렌더링이 일어남
}

// react 18 Promise의 경우
fetch(/*...*/).then(() => {
    setCount(c => c + 1);
    setFlag(f => !f);
    // 함수가 끝나고 한 번만 리렌더링이 일어남
    });
}

// react 18 setTimeout의 경우
setTimeout(() => {
    setCount(c => c + 1);
    setFlag(f => !f);
    // 함수가 끝나고 한 번만 리렌더링이 일어남
}, 1000);

// react 18 native이벤트핸들러의 경우
elm.addEventListener('click', () => {
  setCount(c => c + 1);
  setFlag(f => !f);
  // 함수가 끝나고 한 번만 리렌더링이 일어남
});

 

## batch를 사용하지 않으려면?

- 일반적으로 일괄 처리는 안전하지만, 일부 코드는 상태 변경 직후 DOM에서 무언가를 읽는 데 의존할 수 있습니다.

- 이러한 사용 사례의 경우 ReactDOM.flushSync()를 사용하여 일괄 처리를 옵트아웃할 수 있습니다.

import { flushSync } from 'react-dom';

function handleClick() {
    flushSync(() => {
        setCount(count + 1);
    });
    // react가 바로 돔 업데이트를 합니다. (리렌더링 일어남)
    
    // 바뀐 count 상태값을 가지고 리렌더링이 끝나고, 이어서 다음 코드를 실행합니다.
    
    flushSync(() => {
        setClicked(true);
    });
    // react가 바로 돔 업데이트를 합니다. (리렌더링 일어남)
}

 

## 정리

리액트 18 버전에서 Automatic batching
1. 더 나은 성능을 위한 더 적은 리렌더링을 합니다.
2. 이벤트 핸들러 밖에서도 작동합니다.
3. 필요할 때는 제외할 수 있습니다.

 

 

참고

https://react.dev/blog/2022/03/29/react-v18

https://github.com/reactwg/react-18/discussions/21

'개인노트-개인공부' 카테고리의 다른 글

Vite란?  (1) 2023.12.25
Zustand  (2) 2023.12.17
리팩토링(Refactoring)  (2) 2023.11.26
JWT(JSON Web Token)  (2) 2023.11.19
OAuth  (1) 2023.11.12