React 19 useActionState 살펴보기
Action
: 비동기 전환을 사용하는 함수
(Before) Example1. passive process
function OrderCoffee({}) {
const [coffee, setCoffee] = useState("");
const [error, setError] = useState(null);
const [isPending, setIsPending] = useState(false);
const handleSubmit = async () => {
setIsPending(true);
const error = await orderCoffee(coffee);
setIsPending(false);
if (error) {
setError(error);
return;
}
redirect("/order-success");
};
return (
<div>
<input value={coffee} onChange={(event) => setCoffee(event.target.value)} />
<button onClick={handleSubmit} disabled={isPending}>
Order Coffee
</button>
{error && <p>{error}</p>}
</div>
);
}
데이터를 변경하고, 이에 대응하여 상태를 업데이트할 때, 예를 들어, 사용자가 이름을 변경하는 양식을 제출할 때 API 요청을 수행한 후 응답을 처리하는 경우 과거에는 보류 상태, 오류, 낙관적 업데이트, 순차적 요청 등을 수동으로 처리해야 했습니다.
(react-19) Example2. async transition
// Using pending state from Actions
function OrderCoffee({}) {
const [coffee, setCoffee] = useState("");
const [error, setError] = useState(null);
const [isPending, startTransition] = useTransition();
const handleSubmit = () => {
startTransition(async () => {
const error = await orderCoffee(coffee);
if (error) {
setError(error);
return;
}
redirect("/order-success");
})
};
return (
<div>
<input value={coffee} onChange={(event) => setCoffee(event.target.value)} />
<button onClick={handleSubmit} disabled={isPending}>
Order Coffee
</button>
{error && <p>{error}</p>}
</div>
);
}
async transition은 즉시 isPending 상태를 true로 설정하고, 비동기 요청을 수행한 후, 모든 전환이 끝나면 isPending을 false로 전환합니다. 이를 통해 데이터가 변경되는 동안 현재 UI를 responsive and interactive할 수 있게 만들어줍니다.
`useTransition`은 React의 동시성 모드를 활용하여 상태 업데이트를 비동기적으로 처리하는 기능으로, 내부적으로 React의 스케줄러와 Fiber 아키텍처를 사용하여 작업의 우선 순위를 관리하고, 메인 스레드가 차단되지 않도록 합니다. 이를 통해 사용자 인터페이스의 반응성을 유지하면서 복잡한 상태 업데이트를 효율적으로 처리할 수 있습니다.
(react-19) Example3. useActionState
// Using <form> Actions and useActionState
function OrderCoffee({ coffee, setCoffee }) {
const [error, submitAction, isPending] = useActionState(
async (previousState, formData) => {
const error = await orderCoffee(formData.get("coffee"));
if (error) {
return error;
}
redirect("/order-success");
return null;
},
null,
);
return (
<form action={submitAction}>
<input type="text" name="coffee" />
<button type="submit" disabled={isPending}>Order Coffee</button>
{error && <p>{error}</p>}
</form>
);
}
액션(Actions)을 기반으로, 리액트 19는 일반적인 액션 케이스를 처리하기 위한 새로운 훅 React.useActionState를 도입했습니다. 액션은 데이터를 제출하는 작업을 자동으로 관리해 줍니다.
useActionState는 함수(“액션”)를 인수로 받아, 호출할 래핑된 액션을 반환합니다. 이는 액션들이 조합되기 때문에 작동합니다. 래핑된 액션이 호출되면, useActionState는 액션의 마지막 결과를 데이터로 반환하고, 액션의 보류 상태를 보류로 반환합니다.
(react-19) Example4. 실제 사용 예시
const [error, submitAction, isPending] = useActionState(
async (previousState, formData) => {
if (!formData) {
setCoffeeImage(null);
return null;
}
const coffeeName = formData.get("coffee");
const shot = formData.get("shot");
const temperature = formData.get("temperature");
const coffeeSrc = await orderCoffee(coffeeName, temperature, shot);
if (!coffeeSrc) {
return "Failed to fetch coffee image";
}
setCoffeeImage(coffeeSrc);
return null;
},
null,
);
참고
React.useActionState는 이전에는 canary release 에서 ReactDOM.useFormState라고 불렸으나, 이름이 변경되었고 useFormState는 폐기되었습니다.