React

React 19.3

원문: React 19.3
— React Blog 원문은 CC-BY-4.0
라이선스로 제공되며, 이 글은 한국어로 번역한 2차 저작물입니다.

새로운 React 기능

View Transitions

새로운
<ViewTransition>
컴포넌트를 사용하면 브라우저의 View Transition API
를 사용하여 요소가 진입, 퇴장, 이동 또는 크기 조정될 때 애니메이션을 적용할 수 있습니다. 작년에 실험적인 API로 공유되었던 이 기능이 19.3에서는 안정화되어 사용할 준비가 되었습니다.
UI의 일부를 애니메이션하려면
<ViewTransition>
으로 감싸십시오.
import { ViewTransition } from 'react';{isShowing && (<ViewTransition><Component /></ViewTransition>)}
이제 Transition
으로 표시된 업데이트가 하위 컴포넌트의 스타일을 변경하거나
ViewTransition
이 마운트 또는 언마운트되도록 하면 React가 해당 업데이트를 애니메이션 처리합니다.
React는 트리가 변경된 방식에 따라 실행할 애니메이션을 선택합니다.
  • enter:
    <ViewTransition>
    이 추가됩니다.
  • exit:
    <ViewTransition>
    이 제거됩니다.
  • update:
    <ViewTransition>
    의 자식이 스타일이나 콘텐츠를 변경합니다.
  • share: 명명된
    <ViewTransition>
    이 한 곳에서 제거되고 다른 곳에서 추가됩니다.
Transition으로 표시되지 않은 업데이트는 긴급하며 UI에 즉시 반영되어야 하므로 애니메이션을 트리거하지 않습니다. startTransition
내부의 상태 업데이트,
<Suspense>
표시 또는
useDeferredValue
의 업데이트는 모두 View Transition 애니메이션을 트리거합니다.
다음은 진입/퇴장 애니메이션의 간단한 예입니다.
import { ViewTransition, useState, startTransition } from 'react';
import { Video } from './Video';
import videos from './data';

export default function Component() {
  const \[showItem, setShowItem\] = useState(false);

  return (
    <\>
      <button
        onClick\={() \=> {
          startTransition(() \=> {
            setShowItem((prev) \=> !prev);
          });
        }}\>
        {showItem ? '➖' : '➕'}
      </button\>

      {showItem && (
        <ViewTransition\>
          <Video video\={videos\[0\]} />
        </ViewTransition\>
      )}
    </\>
  );
}


기본적으로
<ViewTransition>
은 부드러운 크로스페이드로 애니메이션 처리됩니다. View Transition Class
를 전달하고 CSS에서 애니메이션을 정의하여 각 애니메이션 종류를 사용자 정의하거나, Web Animations API
를 사용하여 이벤트 props
(
onEnter
,
onExit
,
onShare
,
onUpdate
)로 명령적으로 애니메이션을 트리거할 수 있습니다.
현재
<ViewTransition>
은 DOM에서만 작동합니다. React Native 및 기타 플랫폼에 대한 지원을 작업 중입니다.
더 많은 정보는
<ViewTransition>
문서
를 참조하십시오.

addTransitionType

때로는 동일한 상태 업데이트에 대한 애니메이션을 사용자 정의하고 싶을 것입니다. 예를 들어, 캐러셀을 세 번째 슬라이드로 앞으로 탐색하면 슬라이드가 오른쪽에서 왼쪽으로 애니메이션 처리되어야 하지만, 뒤로 탐색하면 현재 슬라이드가 3으로 설정되더라도 왼쪽에서 오른쪽으로 애니메이션 처리됩니다.
상태 업데이트와 함께
addTransitionType
을 호출하여 특정 View Transition의 애니메이션을 사용자 정의할 수 있습니다. 이렇게 하면 특정 전환의 _원인_에 대한 추가 정보를 추가할 수 있습니다.
function nextSlide() {startTransition(() => {addTransitionType('next');setCurrentSlide(c => c + 1);});}function previousSlide() {startTransition(() => {addTransitionType('previous');setCurrentSlide(c => c - 1);});}
그런 다음 해당 전환 유형을 기반으로 다른 애니메이션을 지정할 수 있습니다.
<ViewTransitionenter={{'next': 'from-right','previous': 'from-left',}}exit={{'next': 'to-left','previous': 'to-right',}}><Page /></ViewTransition>
다음은 예시입니다.
import {
  ViewTransition,
  addTransitionType,
  useState,
  startTransition,
  Fragment
} from 'react';
import { Video } from './Video';
import videos from './data';
import './animations.css';

export default function Component() {
  const \[selected, setSelected\] = useState(0)
  const video = videos\[selected\];

  return (
    <\>
      <div className\="button-container"\>
        <button
          onClick\={() \=> {
            startTransition(() \=> {
              addTransitionType('previous');
              setSelected(c \=> c > 0 ? c - 1 : videos.length - 1 )
            });
          }}\>
          ⬅️
        </button\>
        <button
          onClick\={() \=> {
            startTransition(() \=> {
              addTransitionType('next');
              setSelected(c \=> c + 1 < videos.length ? c + 1 : 0)
            });
          }}\>
          ➡️
        </button\>
      </div\>

      <ViewTransition
        key\={video.id}
        enter\={{
          'next': 'from-right',
          'previous': 'from-left'
        }}
        exit\={{
          'next': 'to-left',
          'previous': 'to-right'
        }}
      \>
        <Video video\={video} />
      </ViewTransition\>
    </\>
  );
}


React는 모든 Transition Type을 요소에 브라우저 view transition type
으로 추가하므로
:active-view-transition-type(...)
을 사용하여 CSS에서 애니메이션을 범위 지정할 수 있습니다.
더 알아보려면
addTransitionType
문서
를 참조하십시오.

Suspense를 사용한 대체, 이미지 및 글꼴 애니메이션

React의 View Transitions에서 가장 흥미로운 점 중 하나는 Suspense와의 통합 방식입니다.
<ViewTransition>
으로 감싸서 Suspense 경계가 자식을 표시할 때 애니메이션을 적용할 수 있습니다.
<ViewTransition><Suspense fallback={<Loading />}><Component /></Suspense></ViewTransition>
자식 로딩이 완료되면 React는 대체에서 최종 콘텐츠로 업데이트 애니메이션을 트리거합니다.
예시입니다. ➕를 눌러 처음 렌더링될 때 일시 중지되는 LazyVideo를 렌더링해 보세요.
import { Suspense, useState, startTransition, use, ViewTransition } from 'react';
import { Video, VideoPlaceholder } from './Video';
import { fetchVideo } from './data';

export default function Component() {
  const \[showItem, setShowItem\] = useState(false);

  return (
    <\>
      <button
        onClick\={() \=> {
          startTransition(() \=> {
            setShowItem((prev) \=> !prev);
          });
        }}
      \>
        {showItem ? '➖' : '➕'}
      </button\>

      {showItem && (
        <ViewTransition\>
          <Suspense fallback\={<VideoPlaceholder />}\>
            <LazyVideo />
          </Suspense\>
        </ViewTransition\>
      )}
    </\>
  );
}

function LazyVideo() {
  const video = use(fetchVideo());

  return <Video video\={video} />;
}


이것은 작동하지만, 비디오가 이미 로드되었음에도 불구하고 후속 표시 시에도 비디오가 위아래로 애니메이션 처리되는 것을 알 수 있습니다. (또한 대체가 처음 표시될 때 페이드 인되는 것을 알 수 있습니다.)
일반적으로 Suspense를 사용한 애니메이션은 sparingly 사용될 때 가장 잘 작동하며, 즉시 표시되는 캐시된 UI에는 피하는 것이 좋습니다.
Suspense로 애니메이션 처리할 때 좋은 UX를 달성하기 위한 몇 가지 원칙이 있습니다.
  • 대체는 애니메이션 없이 즉시 표시되어야 합니다.
  • 대체는 애니메이션과 함께 최종 콘텐츠로 업데이트되어야 합니다.
  • 일시 중지되지 않는 자식은 애니메이션 없이 즉시 표시되어야 합니다.
이렇게 하면 UI가 캐시된 경우에도 빠르게 느껴지도록 하고, 대체에서 최종 콘텐츠로의 업데이트를 더 원활하게 만들기 위해 애니메이션을 사용합니다.
위의 예제를 수정하려면 업데이트 외의 모든 애니메이션을 비활성화할 수 있습니다.
<ViewTransition update="auto" default="none"><Suspense fallback={<Fallback />}><Component /></Suspense></ViewTransition>
이제 어떻게 작동하는지 살펴보겠습니다.
import { Suspense, useState, startTransition, use, ViewTransition } from 'react';
import { Video, VideoPlaceholder } from './Video';
import { fetchVideo } from './data';

export default function Component() {
  const \[showItem, setShowItem\] = useState(false);

  return (
    <\>
      <button
        onClick\={() \=> {
          startTransition(() \=> {
            setShowItem((prev) \=> !prev);
          });
        }}
      \>
        {showItem ? '➖' : '➕'}
      </button\>

      {showItem && (
        <ViewTransition update\="auto" default\="none"\>
          <Suspense fallback\={<VideoPlaceholder />}\>
            <LazyVideo />
          </Suspense\>
        </ViewTransition\>
      )}
    </\>
  );
}

function LazyVideo() {
  const video = use(fetchVideo());

  return <Video video\={video} />;
}


버튼을 탭할 때 대체가 즉시 나타나 UI가 사용자 작업에 반응하는 느낌을 유지하는 것을 볼 수 있습니다. 또한 비디오가 로드되면 토글하는 것이 즉시 작동합니다.
달성하려는 효과에 따라 사용할 수 있는 다른 패턴도 있습니다. 더 알아보려면 Suspense로 애니메이션 처리하는 방법에 대한 문서를 확인하세요.

대체 애니메이션 외에도 View Transitions는 이미지 또는 글꼴을 로드하는 동안 Suspense를 트리거하는 옵션으로 사용할 수 있습니다.
이를 통해 브라우저의 기본 동작(이미지 또는 글꼴이 로딩을 마칠 때 깜박일 수 있음)을 방지하고 대신 컴포넌트의 모든 리소스를 고려하는 조정된 로딩 시퀀스를 구축할 수 있습니다.
로드되는 동안 Suspense를 트리거하려면 이미지 또는 글꼴을
<ViewTransition>
안에 래핑하십시오.
<ViewTransition><Suspense fallback={<Fallback />}><img src={imageSrc} /><style href={fontSrc} precedence="default">{`@font-face {        font-family: 'Fancy';        src: url(${fontSrc}) format('truetype');        font-display: swap;      }`}</style></Suspense></ViewTransition>
다음은 데이터, 이미지 및 글꼴이 모두 로드될 때까지 일시 중지되는 컴포넌트의 예입니다.
import { ViewTransition, Suspense, use, useState, startTransition } from 'react';
import { fetchQuote } from './data.js';
import { freshStylesheetUrl, freshImageUrl } from './resources.js';
import { ProfileCard, ProfileCardLoading } from './ProfileCard.js';
import { VanillaProfileCard } from './VanillaProfileCard.js';

export default function App() {
  const \[resources, setResources\] = useState(null);
  return (
    <\>
      <button
        onClick\={() \=> {
          startTransition(() \=> {
            setResources({
              quotePromise: fetchQuote(),
              stylesheet: freshStylesheetUrl(),
              image: freshImageUrl(),
            });
          });
        }}\>
        Show profile
      </button\>

      {resources && (
        <ViewTransition update\='auto' default\='none'\>
          <Suspense fallback\={<ProfileCardLoading />}\>
            <ProfileCard resources\={resources} />
          </Suspense\>
        </ViewTransition\>
      )}

      <hr />

      <VanillaProfileCard />
    </\>
  );
}


이미지, 글꼴 또는 스타일시트 로드를 기다리는 것에 대해 자세히 알아보려면 Suspense 문서
를 참조하십시오.

Fragment Refs

컴포넌트의 DOM 노드에 대한 저수준 제어가 필요한 경우(예: 이벤트 리스너 연결, 가시성 관찰 또는 초점 이동) 일반적으로 ref를 사용할 수 있습니다. 하지만 다음과 같은 경우 이 작업이 어려운 경우가 있습니다.
  • 단일 부모 없이 형제 집합을 렌더링하는 컴포넌트
  • ref
    prop을 다른 요소로 전달하지 않는 컴포넌트
function Component() {// 이 컴포넌트에서 렌더링된 DOM 노드 목록을 어떻게 처리할 수 있을까요?return ({posts.map(post => (<Heading key={post.id}>{post.title}</Heading>))})}
ref를 보유하기 위해 래퍼
<div>
를 추가하는 것이 때로는 작동하지만 컴포넌트의 스타일이나 레이아웃을 방해할 수도 있습니다. 또한 컴포넌트가
ref
prop을 노출하지 않는 경우 컴포넌트를 수정해야 할 수 있으며, 이는 제어하지 않는 라이브러리에서 오는 경우 불가능할 수 있습니다.
Fragment Refs는 이러한 문제를 해결하여 React 컴포넌트가 렌더링하는 것과 상관없이 모든 React 컴포넌트와 작동하는 제한된 수의 일반적인 DOM 메서드를 제공합니다.
19.3에서는
ref
<Fragment>
에 직접 전달하여 사용할 수 있습니다. 이 ref는 Fragment의 DOM 자식을 처리하는 데 사용할 수 있는
FragmentInstance
를 제공합니다.
function Component() {const fragmentRef = useRef(null);useEffect(() => {const fragmentInstance = fragmentRef.current;fragmentInstance.focus();}, []);return (<Fragment ref={fragmentRef}>{posts.map(post => (<Heading key={post.id}>{post.title}</Heading>))}</Fragment>)}
FragmentInstance
는 구조를 변경하지 않고 자식의 DOM을 그룹으로 작동합니다.
  • addEventListener
    ,
    removeEventListener
    ,
    dispatchEvent
    는 첫 번째 수준 자식에 대한 이벤트를 관리합니다.
  • focus
    ,
    focusLast
    ,
    blur
    는 깊이 우선으로 중첩된 자식을 통해 초점을 이동합니다.
  • observeUsing
    ,
    unobserveUsing
    IntersectionObserver
    또는
    ResizeObserver
    를 연결합니다.
  • getClientRects
    ,
    getRootNode
    ,
    compareDocumentPosition
    ,
    scrollIntoView
    를 사용하면 조각의 첫 번째 수준 자식의 크기를 측정하고 스크롤할 수 있습니다.
따라서 Fragment Refs를 사용하면 컴포넌트의 내부를 수정하지 않고도 다른 컴포넌트에 동작을 연결하거나 이미 생성된 DOM 구조를 변경하지 않고도 연결할 수 있습니다.
이 예제는
onChange
prop을 가진
InView
컴포넌트를 보여줍니다. 자식이 뷰포트에 진입하거나 퇴장할 때마다 작동합니다.
import { useState } from 'react';
import Card from './Card';
import InView from './InView';

export default function App() {
  const \[isVisible, setIsVisible\] = useState(true);

  return (
    <div className\={isVisible ? 'page visible' : 'page'}\>
      <div className\="filler"\>Scroll down</div\>

      <InView onChange\={setIsVisible}\>
        <Card title\="First section" />
        <Card title\="Second section" />
      </InView\>

      <div className\="filler"\>Scroll up</div\>
    </div\>
  );
}


InView
가 단일 부모 DOM 요소가 없음에도 불구하고
Card
ref
prop을 노출하지 않음에도 불구하고 자식에게 동작을 추가할 수 있는 방법을 확인하십시오.
Fragment Refs를 사용하여 작업하는 것에 대해 자세히 알아보려면
<Fragment>
문서
를 참조하십시오.

새로운 React DOM 기능

browser

앱에서 서버 렌더링을 사용하는 경우 컴포넌트는 두 가지 다른 환경에서 렌더링됩니다.
  • 서버에서 컴포넌트는 초기 HTML을 생성하기 위해 렌더링됩니다.
  • 클라이언트에서 컴포넌트는 이벤트 핸들러를 추가하기 위해 렌더링됩니다.
대부분의 경우 컴포넌트는 초기 클라이언트 렌더링 출력과 일치하는 HTML을 생성할 수 있어야 하므로, 초기 로드 시 최대한 많은 콘텐츠를 사용자에게 표시하면서도 올바르게 hydration되도록 할 수 있습니다.
하지만 드문 경우지만, 컴포넌트가 서버에서 의미 있는 UI를 생성하지 못할 수 있습니다. 예를 들어,
localStorage
와 같은 브라우저 전용 API에 의존하거나 브라우저의 로컬 시간대를 읽을 수 있습니다. 이러한 경우 해당 컴포넌트를 서버 렌더링에서 완전히 제외하는 것이 좋습니다.
이전에는 효과에서 업데이트할 상태를 사용하거나
window
와 같은 브라우저 API의 존재를 확인하여 이를 수행했을 수 있습니다.
function Component() {const [mounted, setMounted] = useState(false);useEffect(() => {setMounted(true)}, [])// ...}function Component() {const isBrowser = typeof window !== 'undefined';// ...}
19.3에서는 React가 이 기술에 대한 1급 API를 포함합니다.
컴포넌트는
use(browser())
를 호출하여 서버 측 렌더링에서 제외될 수 있습니다.
import { use } from 'react';import { browser } from 'react-dom';function Component() {use(browser());// ...}
이렇게 하면 서버에서 Suspense가 트리거되지만 클라이언트에서는 트리거되지 않습니다. 서버 측 렌더링 중에 가장 가까운 Suspense 경계의 대체가 HTML에 표시됩니다. 컴포넌트가 클라이언트에서 hydration된 후
use(browser())
는 일시 중지되지 않아 컴포넌트가 정상적으로 렌더링을 계속할 수 있습니다.
다음은 장치의 로컬 시간대를 렌더링하는 컴포넌트의 예입니다. Reload를 눌러 초기 HTML과 React의 첫 번째 클라이언트 렌더링을 확인하십시오.
import { Suspense, use } from 'react';
import { browser } from 'react-dom';

function TimeZone() {
  use(browser());
  const timeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone;

  return <p\>{timeZone}</p\>
}

export default function App() {
  return (
    <\>
      <p\>Your current time zone is:</p\>
      <Suspense fallback\="Loading..."\>
        <TimeZone />
      </Suspense\>
    </\>
  );
}


TimeZone이 서버에서 일시 중지되기 때문에 초기 HTML에는 Suspense 대체가 포함됩니다. 짧은 인위적인 지연 후 React가 페이지를 hydration하여 컴포넌트가 브라우저에서 정상적으로 렌더링될 수 있도록 합니다.
따라서 서버 렌더링 중에 의미 있는 UI를 생성하지 못하는 컴포넌트의 경우
browser
를 사용하면 Suspense를 로딩 상태에 사용하여 다른 컴포넌트와 통합되어 준비될 때까지 렌더링을 일시 중지할 수 있습니다.

use
에 대한 다른 호출과 마찬가지로
use(browser())
는 조건문 내에서 또는 조기 반환 후에 호출할 수 있습니다. 이렇게 하면 컴포넌트 또는 사용자 정의 Hooks를 작성하여 prop 값과 같은 조건에 따라 서버 렌더링을 제외할 수 있습니다.
위의 동일한 예제를 사용하되, 이제 TimeZone 컴포넌트가 초기 HTML의 일부로 렌더링할 수 있는 선택적 기본값을 허용하도록 하겠습니다.
import { Suspense, use } from 'react';
import { browser } from 'react-dom';

function TimeZone({ defaultValue }) {
  if (defaultValue) {
    return <p\>{defaultValue}</p\>;
  }

  use(browser());
  const localTimeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone;

  return <p\>{localTimeZone}</p\>
}

export default function App() {
  return (
    <\>
      <div\>
        <p\>The event's time zone is:</p\>
        <TimeZone defaultValue\='America/New\_York' />
      </div\>

      <hr />

      <div\>
        <p\>Your current time zone is:</p\>
        <Suspense fallback\="Loading..."\>
          <TimeZone />
        </Suspense\>
      </div\>
    </\>
  );
}


TimeZone이 두 번째 경우, 즉 기본값이 제공되지 않을 때만 일시 중지되는 것을 볼 수 있습니다.
이 패턴의 또 다른 유용한 예는
useQuery
와 같은 데이터 가져오기 Hook을 서버 렌더링에서 제외하는 것입니다. 단, 해당 쿼리의 초기 데이터가 (예: 서버 컴포넌트 또는 프레임워크의 로더 함수에서) 전달된 경우를 제외합니다.
function useBrowserQuery(query, options) {if (options.initialData === undefined) {use(browser());}return useQuery(query, options);}function ProductDetails({ productId, initialData }) {const product = useBrowserQuery(`/api/products/${productId}`, {initialData,});return <h1>{product.name}</h1>;}
이제 ProductDetails 컴포넌트는
initialData
가 서버 렌더링 중에 제공되는 경우 HTML에 포함될 수 있습니다. 그렇지 않으면 브라우저에서 렌더링될 때까지 일시 중지되며, 그 후
useQuery
는 정상적으로 데이터를 가져오거나 캐시에서 읽을 수 있습니다.
browser
에 대해 자세히 알아보려면 문서를 확인하십시오.

Trusted Types 지원

React 19.3은 브라우저 Trusted Types API
와 통합됩니다. 이 보안 기능은 DOM 기반 XSS 공격을 방지하는 데 도움이 됩니다. 사이트가
Content-Security-Policy: require-trusted-types-for 'script'
로 Trusted Types를 시행하는 경우, 브라우저는
innerHTML
과 같은 삽입 싱크에 전달되는 값이 일반 문자열이 아닌 해당 위생 정책을 통해 생성된 형식화된 객체 (
TrustedHTML
,
TrustedScript
,
TrustedScriptURL
)여야 한다고 요구합니다. React는 이제 이러한 값을 강제 변환 없이 통과시키므로 브라우저가 유효성을 검사하고 Trusted Types 정책이 의도한 대로 작동합니다.

새로운 React 서버 컴포넌트 기능

<Context>
를 서버 컴포넌트에서 직접 렌더링 가능

서버 컴포넌트가 Context를 _생성_할 수는 없지만,
'use client'
모듈에서 가져와 Context를 _렌더링_할 수 있습니다.
이전에는 클라이언트 모듈이 별도의 래퍼 컴포넌트(종종 Provider라고 함)를 내보내야 했습니다.
// user-context.js'use client';import { createContext } from 'react';export const UserContext = createContext(null);export function UserProvider({ currentUser, children }) {return <UserContext value={currentUser}>{children}</UserContext>;}
// server-component.jsimport { UserProvider } from './user-context';export async function Layout({ children }) {const currentUser = await getCurrentUser();return (<UserProvider currentUser={currentUser}>{children}</UserProvider>)}
이 예제에서 Provider는 서버 컴포넌트의 prop을 Context에 직접 전달하는 것 외에는 아무것도 하지 않는다는 점에 유의하십시오.
React 19.3에서는 서버 컴포넌트가 추가 래핑 컴포넌트 없이
'use client'
모듈에서 직접 Context를 가져와 렌더링할 수 있습니다.
// user-context.js'use client';import { createContext } from 'react';export const UserContext = createContext(null);
// server-component.jsimport { UserContext } from './user-context';export async function Layout({ children }) {const currentUser = await getCurrentUser();return (<UserContext value={currentUser}>{children}</UserContext>)}
이것은 서버 컴포넌트가 나머지 클라이언트 트리에 데이터를 공유할 수 있도록 하는 Context에 특히 유용합니다.

변경 로그

기타 주목할 만한 변경 사항
  • react
    : 느린 Transition이 관련 없는 Transition을 방해하지 않도록 Transition을 단일 렌더링으로 얽매는 대신 독립적으로 렌더링합니다.
  • react
    : 조건문에서
    use
    를 잘못 사용했을 때 경고를 추가합니다.
  • react
    :
    useActionState
    오류 메시지에서 "form state"를 "action state"로 이름을 변경합니다.
  • react-dom
    :
    onFullscreenChange
    onFullscreenError
    이벤트 지원을 추가합니다.
  • react-dom
    :
    maskType
    SVG 속성에 대한 지원을 추가합니다.
  • react-dom
    : 모듈 리소스에 대해
    fetchPriority
    를 지원합니다.
  • react-dom
    : 서버 작업 후 React가 양식을 자동으로 재설정할 때
    onReset
    을 발생시킵니다.
  • react-dom
    :
    submit
    이벤트에
    submitter
    를 포함합니다.
  • react-dom
    : iframe에서
    credentialless
    를 부울 속성으로 인식합니다.
  • react-dom
    : 다음 프레임까지
    resize
    이벤트의 업데이트를 배치합니다.
  • react-server
    :
    Error.cause
    AggregateError.errors
    를 클라이언트로 전송합니다.
  • react-server
    : Flight에서
    <Activity>
    를 지원합니다.
주목할 만한 버그 수정
  • react
    :
    useDeferredValue
    가 오래된 값에 고정되는 문제를 수정합니다.
  • react
    : Suspense 대체로의 컨텍스트 전파 및 일시 중지된 Suspense 경계를 통한 컨텍스트 전파를 수정합니다.
  • react
    : 숨겨진 트리 내에서 비활성화된 Suspense 경계를 업데이트할 때 발생하는 중단 문제를 수정합니다.
  • react
    :
    <Activity>
    트리가 숨겨져 있는 동안 발생한
    useSyncExternalStore
    의 저장소 변경 사항 누락 문제를 수정합니다.
  • react
    :
    useEffectEvent
    forwardRef
    memo
    컴포넌트에서 최신 값을 읽도록 수정합니다.
  • react
    : 컴포넌트 상태가 업데이트될 때 폼 상태가 재설정되는 문제를 수정합니다.
  • react
    :
    lazy
    ,
    memo
    및 컴포넌트 종류를 변경하는 편집 시 Fast Refresh의 여러 버그를 수정합니다.
  • react
    :
    <Activity>
    에 포함된
    <title>
    <title>
    이 포함된
    <Activity>
    visible
    에서
    hidden
    모드로 변경된 후에도
    <head>
    로 계속 호이스트되는 버그를 수정합니다.
  • react
    : 숨겨진
    <Activity>
    에서 오류가 벗어나지 않도록 합니다.
  • react
    : 숨겨진
    <Activity>
    내에서 렌더링된 포털 콘텐츠를 숨깁니다.
  • react
    : 오류 메시지에서 내부
    <Offscreen>
    유형을 참조하지 않도록 합니다.
  • react-dom
    : 위임되거나 이미 초점이 맞춰진 요소의 초점 문제를 수정합니다.
  • react-dom
    : DOM 사양에 따라 캡처 옵션을 정규화하여
    FragmentInstance
    리스너 누수를 수정합니다.
  • react-dom
    : 모바일 사파리에서
    <ViewTransition>
    충돌을 수정합니다.
  • react-dom
    :
    SuspenseList
    에서
    <ViewTransition>
    충돌을 수정합니다.
  • react-dom
    :
    type="number"
    입력의
    defaultValue
    를 다른 입력 유형과 일치하도록 업데이트합니다.
  • react-dom
    :
    innerHTML
    이 변경되지 않은 경우 설정하지 않도록 합니다.
  • react-dom
    :
    nonce
    속성에서 잘못된 hydration 불일치를 수정합니다.
  • react-dom
    : Deno에서
    react-dom/server
    가 중단되는 문제를 수정합니다.
  • react-server
    :
    decodeReplyFromBusboy
    에서 누락된
    FormData
    항목을 수정합니다.
  • react-server
    : 깊은 비동기 체인에서의 스택 오버플로 및 지수형 디버그 정보 증가로 인한
    RangeError
    를 수정합니다.
모든 변경 사항 목록은 변경 로그를 참조하십시오.

이 게시물을 작성한 Sam Selikoff
님, 그리고 검토해 주신 Matt Carroll
, Dan Abramov
, Andrew Clark
님께 감사드립니다.