본문 바로가기
C.W.K.
Stream
Lesson 03 of 08 · published

React 19의 useActionState

~22 min · useActionState, form state, errors

Level 0호기심
0 XP0/68 lessons0/11 achievements
0/120 XP to next level120 XP to go0% complete

이름과 import 위치가 바뀌었어

예전 react-domuseFormState 대신 React 19에서는 reactuseActionState를 써. 새 hook은 isPending까지 세 번째 값으로 돌려주므로, 흔한 제출 버튼 비활성화만을 위해 별도 useFormStatus를 붙일 필요가 줄었어.

세 값이 한 제출 흐름을 만든다

const [state, formAction, isPending] = useActionState(action, initialState)처럼 사용해. state는 화면에 보여 줄 최근 결과, formAction은 폼에 연결할 함수, isPending은 요청이 진행 중인지 알려 주는 값이야.

액션의 첫 인자는 이전 상태야

연결한 액션은 (prevState, formData)를 받고 다음 상태를 반환해. 성공 메시지, 필드별 검증 오류, 낙관적 표시를 되돌릴 정보가 이 통로를 지나와. 반환값의 타입을 명시하면 서버와 클라이언트가 같은 상태 모양을 공유할 수 있어.

상태는 응답이지 데이터베이스가 아니야

여기에 영구 데이터를 복제하지 말고, “이번 제출이 어떻게 끝났는가”를 표현하는 데 써. 실제 데이터는 변경 뒤 캐시를 재검증해 다시 읽는 편이 일관돼.

액션 상태를 성공·필드 오류·전체 오류처럼 구별 가능한 union으로 정의해. 그러면 렌더링 쪽은 문자열을 추측하지 않고 상태 종류에 따라 focus와 aria 설명을 이동할 수 있어. isPending 동안 중복 제출을 막되 키보드 사용자가 왜 버튼이 잠겼는지 알 수 있는 표시도 남겨. 연속 제출과 느린 응답도 시험해서 오래된 결과가 새 상태를 덮지 않는지 확인해. 대기 표시는 장식이 아니라 어떤 제출이 현재 화면을 소유하는지 보여 주는 상태야. pending이 false로 돌아왔다고 서버 데이터가 자동으로 최신이라는 뜻은 아니야. 액션이 변경 뒤 관련 캐시를 재검증하고 새 서버 결과가 렌더링되어야 실제 상태와 맞아. hook 상태에 영구 데이터를 복제하면 두 진실 사이의 동기화 문제가 생겨.

성공, 필드 오류, 서버 오류를 차례로 반환하는 액션을 연결하고 stateisPending의 변화를 기록해. 빠르게 두 번 제출했을 때 버튼과 메시지가 어떻게 움직이는지, 캐시 재검증 뒤 실제 서버 데이터가 상태와 일치하는지도 확인해.

Code

회원가입 폼에서 useActionState 사용하기·tsx
'use client';
import { useActionState } from 'react';

async function signUp(prev: any, formData: FormData) {
  'use server';
  const email = formData.get('email') as string;
  if (!email.includes('@')) return { error: 'Invalid email', success: false };

  await db.user.create({ data: { email } });
  return { error: null, success: true };
}

export default function SignUpForm() {
  const [state, formAction, pending] = useActionState(signUp, {
    error: null,
    success: false,
  });

  return (
    <form action={formAction} className="space-y-2">
      <input name="email" type="email" required className="border px-2 py-1" />
      {state.error && <p className="text-red-500 text-sm">{state.error}</p>}
      {state.success && <p className="text-green-600 text-sm">Welcome!</p>}
      <button disabled={pending} className="rounded bg-blue-600 px-3 py-1 text-white">
        {pending ? 'Signing up&hellip;' : 'Sign up'}
      </button>
    </form>
  );
}

External links

Exercise

성공·필드 오류를 돌려주는 액션을 useActionState에 연결해. state, formAction, isPending의 변화와 캐시 갱신 뒤 실제 데이터 일치를 확인해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.