[React] useState() 동작 방식 (batch)

Dan·2022년 12월 8일
0

React

목록 보기
3/6

우선은 왜 ‘비동기적으로’ 처리하는지?

  • 만약 setState와 같은 hook 함수들이 동기적으로 state를 업데이트 하고 랜더링 한다면?
  • state가 바뀔 때마다 랜더링을 해주기 때문에 100번 setState를 호출하면 100번 렌더링을 해야 한다.
  • 비동기적으로 처리하는 과정에서 리액트는 state 변화를 묶어서(batch)처리한다.
  • batch update는 16ms 간격으로 진행한다.
  • 해당 간격 동안 변화하는 상태를 모아 한 번에 랜더링 하는 것이다.
  • 이는 불필요한 렌더링 횟수를 줄여 좀 더 빠른 속도로 동작하게 한다.

setState실행 과정 요약

  1. 컴포넌트에서 setState를 실행하면 컴포넌트 외부의 값을 변경하는 것이기 때문에(클로져), 상태가 변경된 직후 리렌더링 되기 전까지는 이전의 값을 그대로 참조한다.

  2. 컴포넌트가 다시 랜더링 될 때 useState를 호출해서 변경된 외부 값을 가져오고 화면에 반영한다.

  3. 이를 예시 코드로 써보면 다음과 같다.

    let _value;
    
    export useState(initialValue){
      if (_value === 'undefined') {
      //초기값 설정
        _value = initialValue;
      }
      const setValue = newValue => {
      //set함수로 전역에 있는 _value값을 재할당
        _value = newValue;
      }
    
      //리렌더링 되고 useState()가 다시 호출될때 전역에 있는 _value를 반환
      return [_value, setValue];
    }
  4. 여기서 setState()를 연속적으로 호출할 때 이에 대한 처리가

  5. 값으로 인자를 전달할 때와, 함수로 전달할 때가 다르다.

값으로 전달

  1. setState()에 인자로 값을 전달하면 batch에 묶인 상태변화들 중 마지막의 상태변화 로직만 반영한다. (덮어쓰기)

  2. 즉, 마지막에 호출된 setState가 앞선 setState들을 덮어 쓴다.

    
    const [count, setCount] = useState(0);
    
        const incrementCount = () => {
          setCount(count + 1);
          setCount(count + 4);
        };
    
    //리랜더링 했을 때 count는 0+4= 4가 된다. 
    // setCount(+1)은 setCount+4에 덮여쓰여진다. 

함수로 전달


    const [count, setCount] = useState(0);

    const incrementCount = () => {
      setCount((count) => count + 1);
      setCount((count) => count + 1);
    };

함수를 매개변수로 넘기면 batch로 묶인 상태변화들을 queue(FIFO)에 담아서 처리한다.

facebook 개발자 (redux만든사람 트윗)

함수를 매개변수로 setState()를 여러번 호출하는 것은 안전하다. 상태 업데이트들은 큐에 저장되어 호출된 순서대로 실행 될 것이다.

batch가 안되는 경우 (react 18버전에서는 batch 되는걸로 확인)

  • 그렇다고 setState의 호출들이 항상 묶여서 처리 되는 것은 아니다.
  • async/awaitthen/catchsetTimeoutfetch 와 같이 비동기적인 태스크를 실행할 때는 묶여서 처리되지 않는다. (각각의 setState마다 랜더링이 일어난다)
      const [counter1, setCounter1] = useState(0);
      const [counter2, setCounter2] = useState(0);
      const [counter3, setCounter3] = useState(0);
      const [renderCount, setRenderCount] = useState(0);
    
      useEffect(() => {
        setRenderCount(renderCount + 1);
      }, [counter1, counter2, counter3]);
    
      const handleClickAsync = async () => {
        await setCounter1(counter1 + 1);
        setCounter2(counter2 + 1);
        setCounter3(counter3 + 1);
      }
    
      const handleClickThen = () => {
        Promise.resolve().then(res => {
          setCounter1(counter1 + 1);
          setCounter2(counter2 + 1);
          setCounter3(counter3 + 1);
        });
      }
    // 두 함수 모두 3번의 랜더링을 일으킨다. 
    
    //CODE SANDBOX
    [https://codesandbox.io/s/vibrant-worker-11odn?from-embed=&file=/src/App.js:109-725](https://codesandbox.io/s/vibrant-worker-11odn?from-embed=&file=/src/App.js:109-725)
  • 하지만 ReactDOM.unstable_batchUpdate 를 사용하면 batch를 강제할 수 있다.
      const [counter1, setCounter1] = useState(0);
      const [counter2, setCounter2] = useState(0);
      const [counter3, setCounter3] = useState(0);
      const [renderCount, setRenderCount] = useState(0);
    
      useEffect(() => {
        setRenderCount(renderCount + 1);
      }, [counter1, counter2, counter3]);
    
      const handleClickThen = () => {
        Promise.resolve().then(res => {
          ReactDOM.unstable_batchedUpdates(() => {
          setCounter1(counter1 + 1);
          setCounter2(counter2 + 1);
          setCounter3(counter3 + 1);
        });
      });
    }
    //한번의 랜더링만 일으킨다. 
    
    //CODE SANDBOX
    [https://codesandbox.io/s/brave-keldysh-eq7zr?from-embed=&file=/src/App.js:143-664](https://codesandbox.io/s/brave-keldysh-eq7zr?from-embed=&file=/src/App.js:143-664)
  • 리액트에서도 이를 인지하고 있다고 Dan Abramo는 다음과 같은 말을 했었다.

    “In future versions (probably React 17 and later), React will batch all updates by default so you won’t have to think about this”, according to Dan Abramov.

  • react 18.2.0. 에서 batch 되는거 확인 !

참고자료

https://medium.com/swlh/react-state-batch-update-b1b61bd28cd2

https://velog.io/@juunghunz/ReactuseState-setState-인자-값-함수

https://velog.io/@seongkyun/React의-setState가-비동기-처리되는-이유

https://taenami.tistory.com/m/49

https://lucybain.com/blog/2016/react-state-vs-pros/

0개의 댓글