React 19 베타 버전이 npm을 통해 사용 가능합니다. 이 버전은 주로 라이브러리 개발자들이 React 19에 대비할 수 있도록 하기 위한 것입니다. 앱 개발자들은 안정적인 React 19가 출시될 때까지 React 18.3.0을 사용하는 것이 좋습니다.
데이터 변형 후 상태 업데이트를 쉽게 처리할 수 있도록 useTransition과 같은 기능이 도입되었습니다.
예를 들어, 이름 업데이트와 같은 API 요청 후 자동으로 처리되는 에러 상태와 펜딩 상태를 관리할 수 있습니다.
비동기 요청이 진행 중일 때 최적의 상태를 즉시 보여주고, 업데이트가 완료되거나 오류가 발생하면 원래 상태로 자동으로 돌아갑니다.
Actions를 사용하여 데이터 제출 과정을 자동으로 관리하고, 폼 요소에 함수를 전달할 수 있는 기능을 포함하고 있습니다.<form>
, <input>
, <button>
요소에 함수를 action
과 formAction
속성으로 전달하여 폼을 자동으로 제출할 수 있습니다.
폼의 상태를 컨텍스트 없이 직접 액세스할 수 있게 하는 새로운 훅으로, 디자인 시스템에서 유용하게 사용될 수 있습니다.
use API를 통해 리소스를 렌더링 중에 읽을 수 있습니다. 예를 들어, 프로미스를 사용하여 비동기 데이터를 처리할 때까지 렌더링을 중단할 수 있습니다.
빌드 시간에 미리 컴포넌트를 렌더링하거나 각 요청에 따라 별도의 서버 환경에서 실행할 수 있는 새로운 옵션입니다.
클라이언트 컴포넌트가 서버에서 실행되는 비동기 함수를 호출할 수 있게 합니다.
함수 컴포넌트에서 ref를 prop으로 직접 접근할 수 있게 되었습니다.
클라이언트와 서버 사이의 HTML 불일치를 보다 명확하게 식별하고 보고합니다.
<Context.Provider> 대신 를 직접 사용하여 프로바이더를 렌더링할 수 있습니다.
컴포넌트가 DOM에서 제거될 때 클린업 함수를 반환하여 ref를 초기화할 수 있습니다.
내부적으로 스타일시트와 스크립트 태그를 더 적절하게 처리하여, 컴포넌트 로딩 시 스타일과 스크립트의 종속성을 관리합니다.
초기 페이지 로드 및 클라이언트 사이드 업데이트를 최적화하기 위해 새로운 API를 도입하여 브라우저가 필요한 리소스를 더 빨리 인식하고 로드할 수 있게 합니다.
업그레이드 가이드를 참조하여 새로운 기능들을 단계별로 적용할 수 있습니다.
이전에는 사용자가 이름을 변경하는 폼을 제출했을 때, 비동기 요청을 처리하고 결과에 따라 상태를 업데이트하는 과정에서 많은 상태 관리가 필요했습니다.
function UpdateName() {
const [name, setName] = useState("");
const [error, setError] = useState(null);
const [isPending, setIsPending] = useState(false);
const handleSubmit = async () => {
setIsPending(true);
try {
await updateName(name);
redirect("/success");
} catch (error) {
setError(error);
}
setIsPending(false);
};
return (
<div>
<input value={name} onChange={(event) => setName(event.target.value)} />
<button onClick={handleSubmit} disabled={isPending}>Update Name</button>
{error && <p>Error: {error}</p>}
</div>
);
}
React 19에서는 useTransition을 사용하여 비동기 요청의 상태 관리를 간소화할 수 있습니다. 상태 업데이트가 자동으로 관리되므로 코드가 훨씬 간결해집니다.
function UpdateName() {
const [name, setName] = useState("");
const [error, setError] = useState(null);
const [isPending, startTransition] = useTransition();
const handleSubmit = () => {
startTransition(async () => {
try {
await updateName(name);
redirect("/success");
} catch (error) {
setError(error);
}
});
};
return (
<div>
<input value={name} onChange={(event) => setName(event.target.value)} />
<button onClick={handleSubmit} disabled={isPending}>Update Name</button>
{error && <p>Error: {error}</p>}
</div>
);
}
비동기 요청을 처리하고 폼을 제출하는 과정을 직접 구현해야 했습니다.
function UpdateProfile() {
const [formData, setFormData] = useState({ name: "", email: "" });
const [error, setError] = useState(null);
const handleSubmit = async (event) => {
event.preventDefault();
try {
await updateProfile(formData);
// handle success
} catch (error) {
setError(error.toString());
}
};
return (
<form onSubmit={handleSubmit}>
<input
value={formData.name}
onChange={e => setFormData({ ...formData, name: e.target.value })}
placeholder="Name"
/>
<input
value={formData.email}
onChange={e => setFormData({ ...formData, email: e.target.value })}
placeholder="Email"
/>
<button type="submit">Update Profile</button>
{error && <p>{error}</p>}
</form>
);
}
useActionState을 사용하여 데이터 제출 과정을 간소화하고, 폼의 상태 관리를 자동화합니다.
function UpdateProfile() {
const [formData, setFormData] = useState({ name: "", email: "" });
const [error, submitAction, isPending] = useActionState(async (prevState) => {
try {
await updateProfile(formData);
// handle success
} catch (error) {
return error.toString(); // Return error for useActionState to handle
}
});
return (
<form action={submitAction}>
<input
name="name"
defaultValue={formData.name}
onChange={e => setFormData({ ...formData, name: e.target.value })}
placeholder="Name"
/>
<input
name="email"
defaultValue={formData.email}
onChange={e => setFormData({ ...formData, email: e.target.value })}
placeholder="Email"
/>
<button type="submit" disabled={isPending}>Update Profile</button>
{error && <p>{error}</p>}
</form>
);
}
폼의 상태를 관리하기 위해 상위 컴포넌트에서 props를 통해 상태를 전달해야 했습니다.
function Form() {
const [isSubmitting, setIsSubmitting] = useState(false);
return (
<div>
<SubmitButton isSubmitting={isSubmitting} />
</div>
);
}
function SubmitButton({ isSubmitting }) {
return <button disabled={isSubmitting}>Submit</button>;
}
useFormStatus를 사용하여 폼의 상태를 직접 액세스하고, 상태 전달 없이 컴포넌트를 단순화합니다.
function Form() {
return (
<form>
<SubmitButton />
</form>
);
}
function SubmitButton() {
const { isPending } = useFormStatus(); // Directly access form's pending status
return <button disabled={isPending}>Submit</button>;
}
이러한 변화들을 통해 React 19는 개발자가 비동기 상태 관리와 폼 제출을 더 간편하게 처리할 수 있도록 지원하며, 코드를 더 깔끔하고 관리하기 쉽게 만듭니다.
React에서 비동기 데이터를 처리할 때 주로 Suspense와 useEffect를 사용해왔습니다.
function Comments({ commentsPromise }) {
const [comments, setComments] = useState([]);
useEffect(() => {
commentsPromise.then(setComments);
}, [commentsPromise]);
return (
<div>
{comments.map(comment => <p key={comment.id}>{comment.text}</p>)}
</div>
);
}
use API를 사용하여 비동기 리소스를 더 직관적으로 처리할 수 있습니다. 이는 비동기 데이터를 직접적으로 '읽을' 수 있게 해서 Suspense와 연동됩니다.
import { use } from 'react';
function Comments({ commentsPromise }) {
const comments = use(commentsPromise);
return (
<div>
{comments.map(comment => <p key={comment.id}>{comment.text}</p>)}
</div>
);
}
이 코드에서 use는 commentsPromise가 해결될 때까지 컴포넌트의 렌더링을 중단하고, 데이터가 준비되면 자동으로 계속됩니다. 이는 컴포넌트 내에서 비동기 데이터를 더 선언적이고 효율적으로 처리할 수 있게 합니다.
사용자가 데이터를 변경하는 요청을 보낼 때까지 변경사항을 보지 못하는 구조였습니다.
function Profile({ userID }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser(userID).then(setUser);
}, [userID]);
function handleNameChange(newName) {
setUser(prev => ({ ...prev, name: newName }));
updateUserName(userID, newName);
}
return (
<div>
<input value={user ? user.name : ''} onChange={(e) => handleNameChange(e.target.value)} />
</div>
);
}
useOptimistic을 사용하여 사용자의 입력을 즉시 반영하고 서버 응답을 기다리면서 최적화된 상태를 유지할 수 있습니다.
function Profile({ userID }) {
const [user, setUser] = useOptimistic(null);
useEffect(() => {
fetchUser(userID).then(data => setUser(data));
}, [userID]);
function handleNameChange(newName) {
setUser(prev => ({ ...prev, name: newName }));
updateUserName(userID, newName).catch(() => {
// 롤백 또는 에러 처리
});
}
return (
<div>
<input value={user ? user.name : ''} onChange={(e) => handleNameChange(e.target.value)} />
</div>
);
}
이 예시에서 useOptimistic은 사용자 인터페이스를 최신 상태로 유지하면서 백그라운드에서 비동기 작업을 처리하도록 도와줍니다. 사용자가 입력한 데이터는 즉시 반영되어 사용자 경험을 크게 개선합니다.
함수 컴포넌트에서 ref를 사용하려면 React.forwardRef를 사용하여 ref를 명시적으로 전달해야 했습니다.
const MyInput = React.forwardRef((props, ref) => (
<input ref={ref} {...props} />
));
function App() {
const inputRef = useRef();
return <MyInput ref={inputRef} placeholder="Enter text here" />;
}
React 19에서는 함수 컴포넌트에 직접 ref를 prop으로 전달할 수 있어, forwardRef를 사용할 필요가 없어졌습니다.
function MyInput({ ref, ...props }) {
return <input ref={ref} {...props} />;
}
function App() {
const inputRef = useRef();
return <MyInput ref={inputRef} placeholder="Enter text here" />;
}
이 변경으로 코드가 더 간결해지고, 컴포넌트의 사용성이 향상되었습니다.
수화(hydration) 과정에서 발생하는 HTML 불일치 오류는 명확한 정보 없이 다수의 오류로 보고되었습니다.
// 예시: 오류 메시지가 구체적이지 않고 사용자가 문제를 파악하기 어려웠습니다.
console.error("Warning: Text content did not match.");
React 19에서는 HTML 불일치가 발생했을 때 상세한 오류 메시지와 함께 불일치 부분을 명확하게 보고합니다.
// 예시: 오류 메시지가 더 상세하고, 어디에서 불일치가 발생했는지를 정확히 알 수 있습니다.
console.error("Hydration mismatch: Server: 'Hello' Client: 'Hallo'");
이로 인해 개발자는 수화 오류를 더 쉽게 식별하고 수정할 수 있습니다.
Context.Provider를 사용하여 컨텍스트 값을 하위 컴포넌트에 전달해야 했습니다.
const MyContext = React.createContext(defaultValue);
function App() {
return (
<MyContext.Provider value="Updated Value">
<ChildComponent />
</MyContext.Provider>
);
}
React 19에서는 태그 자체를 프로바이더로 사용할 수 있습니다.
const MyContext = React.createContext(defaultValue);
function App() {
return (
<MyContext value="Updated Value">
<ChildComponent />
</MyContext>
);
}
이 변경은 Context API를 사용할 때 코드를 더 간결하게 만들어 줍니다.
ref 콜백은 컴포넌트가 마운트될 때 DOM 요소를 참조하기 위해 사용되었으나, 언마운트 시 클린업 로직을 직접 처리해야 했습니다.
function App() {
const ref = useCallback(node => {
if (node !== null) {
// 초기화 로직
}
}, []);
}
React 19에서는 ref 콜백이 클린업 함수를 반환할 수 있게 되어, 컴포넌트가 DOM에서 제거될 때 자동으로 클린업 로직을 실행할 수 있습니다.
function App() {
const ref = useCallback(node => {
if (node !== null) {
// 초기화 로직
return () => {
// 클린업 로직
};
}
}, []);
}
이러한 개선으로 리소스를 더 효율적으로 관리할 수 있게 되었습니다. 이 변경들은 React 19의 사용성을 향상시키고 개발자가 더 효율적으로 코드를 작성할 수 있도록 도와줍니다.