React

React v18 주요 변경점

Automatic Batching

  • batching은 리액트 내부에서 더 나은 퍼포먼스를 위해, 다수의 상태값 업데이트가 이루어짐에도 불구하고 단 한번만 재렌더링이 이루어지도록 그룹화하여 일괄처리한다. 리액트 17버전까지는 event handler 함수 내부에서만 batching이 동작하고 있지만, 리액트 18버전부터는 프로미스 내부, setTimeout 등 batching이 이루어지지 않는 함수들에 default로 탑재가 된다.
// v17

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

  useEffect(() => {
    setTimeout(() => {
      setCount(prev => prev + 1);
      setIsChecked(prev => !prev);
      // 렌더링이 두번 일어난다.
    }, 1000);
  }, []);

// v18

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

  useEffect(() => {
    setTimeout(() => {
      setCount(prev => prev + 1);
      setIsChecked(prev => !prev);
      // 렌더링이 한번 발생한다.
    }, 1000);
  }, []);

  • 만일 Automatic Batching 적용을 원하지 않을 경우, react-dom에서 제공하는 flushSync
    를 사용함으로써 batching을 막을 수 있다.
// v18
import { flushSync } from 'react-dom';

// prevent batching
  const [count, setCount] = useState(0);
  const [isChecked, setIsChecked] = useState(false);

    useEffect(() => {
    setTimeout(() => {
      flushSync(() => {
        setCount(prev => prev + 1);
      })
      flushSync(() => {
        setIsChecked(prev => !prev);
      })
      // 렌더링이 두번 발생한다.
    }, 1000);
  }, []);


 

Transitions

  • Transition은, 리액트에서 긴급, 긴급하지 않은 상태 업데이트를 구분하는 새로운 콘셉트이다.
    • 긴급 업데이트: 상호작용(타이핑, 클릭, 누름 등)을 통해 상탯값이 즉각적으로 일어나는 것을 기대하는 상탯값들을 대상
    • 전환 업데이트: 뷰의 전환, 즉 즉각적인 업데이트가 필요하지 않은 경우
  • 즉, 기존에 사용자 경험을 개선하기 위해 사용했던 debounce, throttle, setTimeout의 기능을 지원하는 내장 api가 추가되었다.
  • useTransition: startTransition과 더불어 pending state를 추적할때 사용하는 hook
  • startTransition: hook이 사용되지 않을때 Transitions을 사용하기 위한 메서드

 

Suspense, SSR

Suspense를 사용하게 되면, date fetching이 오래 걸리는 컴포넌트의 경우, 렌더링이 되기 전 로딩 상태를 지정할 수 있다. 즉 로딩 상태를 나타내는 또다른 상태값에 대한 의존을 해결하도록 도와준다.

  • 기존의 SSR의 경우 해당 화면의 컴포넌트에서 HTML의 모든 요소들이 렌더된 후에 hydration이 가능했지만, Suspense를 통해서 컴포넌트를 선택적으로 렌더 및 hydration이 가능해졌다.
    • 기존 createRoot 대신 hyrdateRoot 사용
    • 기존 리액트는 renderToString()을 활용하여 HTML Streaming(Server에서 HTML 문서를 내려주는것)을 처리하였지만, 리액트 18버전부터 pipeToNodeWritable()를 활용해 HTML코드를 작은 청크로 나눈 후 보내줄 수 있기 때문에 선택적으로 hydration이 가능해졌다.
  • 기존 SSR에서 hydration을 위해서는 모든 HTML 요소들이 렌더링이 되어야 hydration아 가능했다.
  • Suspense를 통해서 컴포넌트를 선택적으로 렌더 및 hydration이 가능해졌다.
<Layout>
  <NavBar />
  <Sidebar />
  <RightPane>
    <Post />
    <Suspense fallback={<Spinner />}>
      <Comments />
    </Suspense>
  </RightPane>
</Layout>

 

useTransition 이외 New Hooks

  • useId
    • 고유한 ID값을 생성함으로써 클라이언트와 서버 사이의 hydration 미스매치를 피할 수 있다.
    • 콜론과 함께 생성되며 토큰이 고유하도록 도와주지만, querySelectorAll과 같은 셀렉터 메서드로서는 접근이 불가한다.
  • useDeferredValue
    • 함수의 우선순위를 지정하는 useTransition와 달리, 값의 업데이트 우선순위를 지정한다. (긴급한 작업이 끝나기 전까지 이전 상태값을 기억하면서 업데이트를 지연시킨다.)
    • useMemo, React.memo와 같이 사용하여 자식 컴포넌트의 렌더를 지연시키는데 사용이 될 수 있다.
  • useSyncExternalStore
    • (공부 후 추가 예정입니다.)
  • useInsertionEffect
    • useLayoutEffect처럼 DOM이 변경되기 전 동기적으로 실행되지만, useLayoutEffect이 실행되기 전에 DOM에 스타일을 주입하기 위해 사용한다.
    • css-in-js library에 사용이 제한되므로 웬만하면 useEffectuseLayoutEffect를 사용하는 것을 권장하고 있다.

 

Reference