Contents
see ListReact 18 Concurrent Features
React 18에서 도입된 Concurrent Rendering은 React가 여러 버전의 UI를 동시에 준비할 수 있게 해주는 핵심 기능입니다. 사용자 경험을 크게 향상시키는 새로운 훅과 API를 제공합니다.
언제 사용하나요?
- 대규모 리스트 렌더링 시 입력 지연 방지
- 페이지 전환 시 이전 페이지를 유지하며 로딩
- 검색 자동완성 등 빈번한 상태 업데이트
- 사용자 인터랙션 우선순위 관리
React 18 업그레이드
// React 18 설치
npm install react@18 react-dom@18
// index.js 변경
import { createRoot } from "react-dom/client";
const container = document.getElementById("root");
const root = createRoot(container);
root.render(<App />);useTransition - 우선순위 낮추기
import { useState, useTransition } from "react";
function SearchPage() {
const [query, setQuery] = useState("");
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
// 긴급: 입력값 즉시 업데이트
setQuery(e.target.value);
// 낮은 우선순위: 검색 결과 업데이트
startTransition(() => {
setResults(searchItems(e.target.value));
});
};
return (
<div>
<input value={query} onChange={handleChange} />
{isPending ? (
<div>검색 중...</div>
) : (
<ResultList results={results} />
)}
</div>
);
}useDeferredValue - 값 지연
import { useDeferredValue, useMemo } from "react";
function ProductList({ searchQuery }) {
// searchQuery의 지연된 버전
const deferredQuery = useDeferredValue(searchQuery);
// 실제 필터링은 지연된 값으로
const filteredProducts = useMemo(
() => products.filter(p =>
p.name.includes(deferredQuery)
),
[deferredQuery]
);
// 지연 중일 때 시각적 피드백
const isStale = searchQuery !== deferredQuery;
return (
<div style={{ opacity: isStale ? 0.5 : 1 }}>
{filteredProducts.map(p => (
<ProductCard key={p.id} product={p} />
))}
</div>
);
}Suspense 향상
import { Suspense, lazy } from "react";
const Comments = lazy(() => import("./Comments"));
const Sidebar = lazy(() => import("./Sidebar"));
function App() {
return (
<div>
<Header />
<Suspense fallback={<Spinner />}>
<main>
<Content />
<Suspense fallback={<CommentSkeleton />}>
<Comments />
</Suspense>
</main>
<Suspense fallback={<SidebarSkeleton />}>
<Sidebar />
</Suspense>
</Suspense>
</div>
);
}Automatic Batching
// React 18에서는 모든 업데이트가 자동 배칭됨
function handleClick() {
// 이전: 각각 리렌더링
// React 18: 한 번만 리렌더링
setCount(c => c + 1);
setFlag(f => !f);
}
// 비동기 함수 안에서도 배칭됨
async function handleSubmit() {
await saveToServer();
setCount(c => c + 1); // 한 번만
setFlag(f => !f); // 리렌더링
}
// 배칭 해제가 필요한 경우
import { flushSync } from "react-dom";
flushSync(() => {
setCount(c => c + 1);
}); // 즉시 리렌더링
flushSync(() => {
setFlag(f => !f);
}); // 다시 리렌더링useId - SSR 안전한 ID 생성
import { useId } from "react";
function FormField({ label }) {
const id = useId();
return (
<div>
<label htmlFor={id}>{label}</label>
<input id={id} />
</div>
);
}
// 여러 ID가 필요한 경우
function ComplexForm() {
const id = useId();
return (
<>
<label htmlFor={id + "-name"}>이름</label>
<input id={id + "-name"} />
<label htmlFor={id + "-email"}>이메일</label>
<input id={id + "-email"} />
</>
);
}주의사항
- Strict Mode에서 컴포넌트가 두 번 렌더링됨
- Concurrent 기능은 createRoot 필수
- 클래스 컴포넌트에서는 일부 기능 제한
- IE 지원 중단됨