React에서 일괄 처리

18013 단어 javascriptreactwebdev

React의 useState hook이 가장 간단한 후크라고 생각할 수 있습니다. 그러나 약간의 복잡성이 있습니다.

일괄 처리란 무엇입니까?



일괄 처리는 setState에 대한 여러 호출이 하나의 상태 업데이트로만 그룹화되는 경우입니다.

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

  useEffect(() => {
    // only output once per click
    console.log({ count, flag });
  }, [count, flag]);

  const handleClick = () => {
    // Here, react will re-render only once
    // Hence, the state updates are `batched`
    setCount(c => c + 1);
    setFlag(f => !f);
  };

  return (
    <div className='App'>
      <button onClick={handleClick}>Click Me!</button>
      <h3 style={{ color: flag ? 'blue' : 'black' }}>Count: {count}</h3>
    </div>
  );
}



  • ✅ See demo (batching inside event handlers) (버튼을 클릭하면 카운트와 플래그가 모두 변경되지만 콘솔 출력은 하나만 표시됨)

  • 왜 일괄 처리 ?


  • 불필요한 재렌더링을 방지하므로 성능이 좋습니다.
  • 모든 구성 요소가 "절반 적용된"상태 업데이트를 렌더링하지 못하도록 하여 버그가 발생할 수 있습니다.

  • 일관되지 않은 일괄 처리 동작



    그러나 React는 일괄 처리에 대해 일관성이 없었습니다. 예를 들어, async function/promise based API 에서,
    React는 업데이트를 일괄 처리하지 않고 독립적인 업데이트가 발생합니다(두 번의 호출 수행setState).

    // little async function
    const sleep = () => new Promise(resolve => setTimeout(resolve, 200));
    
    export default function App() {
      const [flag, setFlag] = useState(true);
      const [count, setCount] = useState(0);
    
      const handleClick = async () => {
        // mimicing some async call
        // (ex, fecthing data from server, etc.)
        await sleep();
    
        setFlag(f => !f);
        setCount(c => c + 1);
      };
    
      useEffect(() => {
        // in this case, two console logs can be seen
        // since `setState` is called inside an asynchronous function
        // So,  React would not batch the updates, and perform two independent updates.
        console.log({ count, flag });
    
        // whenever `flag` or `count` changes, do somethig!
      }, [count, flag]);
    
      return (
        <>
          <h2>React's Batching Behavior while inside async callbacks</h2>;
          <p>Count: {count}</p>
          <button
            onClick={handleClick}
            style={{ backgroundColor: flag ? 'orange' : 'blue', color: '#fff' }}
          >
            Click me!
          </button>
        </>
      );
    }
    



  • ⚠️ See demo (not batching updates inside async function) (버튼을 클릭하면 콘솔에 두 줄이 인쇄됨)

  • 비동기 함수의 강제 일괄 처리


    setState 이벤트 핸들러에서 업데이트를 일괄 처리하도록 하려면 unstable_batchedUpdates(문서화되지 않은 API)를 사용할 수 있습니다.

    import { unstable_batchedUpdates } from 'react-dom';
    
    unstable_batchedUpdates(() => {
      setCount(c => c + 1);
      setFlag(f => !f);
    });
    


    이는 React가 브라우저 이벤트(예: 클릭) 동안 일괄 업데이트에만 사용되기 때문입니다. 하지만 여기에서는 이벤트가 이미 처리된 후(aync 함수에서) 상태를 업데이트하고 있습니다.

    데모는 React 17: forced batching outside of event handlers을 참조하십시오.

    자동 배치 선택 해제



    일부 코드는 상태 변경 직후 DOM에서 무언가를 읽는 것에 의존할 수 있습니다. 이러한 사용 사례의 경우 ReactDOM.flushSync을 사용하여 일괄 처리를 옵트아웃할 수 있습니다.

    이전 예에서 계속해서

    function App() {
      const [count, setCount] = useState(0);
      const [flag, setFlag] = useState(false);
    
      useEffect(() => {
        console.log({ count, flag });
      }, [count, flag]);
    
      const handleClick = () => {
        // setCount((c) => c + 1);
    
        // Force this state update to be synchronous.
        ReactDOM.flushSync(() => setCount(c => c + 1));
        // By this point, DOM is updated.
    
        setFlag(f => !f);
      };
    
      return (
        <div className='App'>
          <button onClick={handleClick}>Click Me!</button>
          <h3 style={{ color: flag ? 'blue' : 'black' }}>Count: {count}</h3>
        </div>
      );
    }
    


    참조⚠️ ReactDOM.flushSync: Opt out of automatic batching in event handlers
  • 그러나 ReactDOM.flushSync는 일반적이지 않으며 드물게 사용해야 합니다.

  • flushSync flushes the entire tree and actually forces complete re-rendering for updates that happen inside of a call, so you should use it very sparingly. This way it doesn’t break the guarantee of internal consistency between props, state, and refs.



    이 API의 비동기 동작 및 이유setState가 비동기인 이유에 대해 자세히 알아보려면 이 멋진 토론RFClarification: why is setState asynchronous? #11527을 확인하십시오.

    React 18의 자동 배치



    React 18은 out-of-the-box improvements과 함께 일부 ReactDOMClient.createRoot 을 포함합니다.
    여기에는 자동 일괄 처리 지원이 포함됩니다.

    Starting in React 18, all updates will be automatically batched, no matter where they originate from.



    따라서 이벤트 핸들러, 비동기 함수, 시간 초과 또는 모든 함수 내부에서 setState를 호출하면 자동으로 일괄 처리됩니다(내부 반응 이벤트와 동일).
    이렇게 하면 렌더링이 줄어들어 반응 애플리케이션의 성능이 향상됩니다.

    function handleClick() {
      fetchSomething().then(() => {
        // React 18 and later DOES batch these:
        setCount(c => c + 1);
        setFlag(f => !f);
        // React will only re-render once at the end (that's batching!)
      });
    }
    


  • 이 자동 일괄 처리 동작은 ReactDOM.createRoot가 있는 React 18에서만 작동합니다.
  • 레거시ReactDOM.render가 포함된 React 18은 이전 동작
  • 을 유지합니다.
  • React 18의 자동 일괄 처리에 대한 자세한 내용은 Automatic batching for fewer renders in React 18 #21을 참조하십시오.

  • 내 블로그에서 읽고 싶습니까? Checkout this blog post

    좋은 웹페이지 즐겨찾기