
startTransition 은 이제 비동기 함수를 지원하며, 이를 통해 데이터가 로딩되는 동안 이전 화면을 유지하다가 부드럽게 새 화면으로 전환합니다.'use client';
import { useTransition, useState } from 'react';
import { fetchTabData } from './actions';
export default function TabSwitcher() {
const [isPending, startTransition] = useTransition();
const [data, setData] = useState(null);
function handleTabClick(tabId: string) {
// [핵심] startTransition으로 상태 업데이트를 감쌉니다.
// React는 이 작업이 완료될 때까지 UI를 차단하지 않고,
// 브라우저의 View Transition API와 연동하여 부드러운 전환을 준비합니다.
startTransition(async () => {
const newData = await fetchTabData(tabId);
setData(newData);
});
}
return (
<div>
<div className="tabs">
<button onClick={() => handleTabClick('home')}>Home</button>
<button onClick={() => handleTabClick('settings')}>Settings</button>
</div>
{/* 데이터 로딩 중에는 투명도를 조절하는 등 시각적 피드백 제공 가능 */}
<div style={{ opacity: isPending ? 0.5 : 1 }}>
{data ? <Content data={data} /> : <p>Select a tab</p>}
</div>
</div>
);
}'use server';
// 이 함수 내부의 로직(DB 접근 등)은 절대 클라이언트로 유출되지 않습니다.
export async function submitForm(formData: FormData) {
const apiKey = process.env.API_KEY; // 서버 환경 변수
await saveToDb(formData, apiKey);
return { success: true };
}useOptimistic 훅을 사용하면 아주 간단해집니다.'use client';
import { useOptimistic, useRef } from 'react';
import { sendMessage } from './actions';
export default function MessageList({ messages }: { messages: string[] }) {
// [핵심] useOptimistic 훅 사용
// optimsticMessages: 화면에 보여줄 임시 상태
// addOptimisticMessage: 임시 상태를 추가하는 함수
const [optimisticMessages, addOptimisticMessage] = useOptimistic(
messages,
(state, newMessage: string) => [...state, newMessage]
);
async function action(formData: FormData) {
const message = formData.get('message') as string;
// 1. 서버 요청을 보내기 전에 화면에 먼저 표시 (낙관적 업데이트)
addOptimisticMessage(message);
// 2. 실제 서버 요청 (실패 시 React가 알아서 상태를 롤백해 줍니다)
await sendMessage(message);
}
return (
<div>
{optimisticMessages.map((m, i) => <div key={i}>{m}</div>)}
<form action={action}>
<input name="message" />
<button type="submit">Send</button>
</form>
</div>
);
}useEffect 내부에서 어떤 값은 useEffectEvent 는 이펙트를 다시 실행시키지 않으면서 최신 값을 읽을 수 있게 해 줍니다.<Activity> 컴포넌트는 화면에서 사라진 컴포넌트를 display: none 처럼 숨겨두면서 상태를 보존합니다.
덕분에 다시 돌아왔을 때 즉시 이전 상태를 보여줄 수 있습니다.