[React] useFormState, useFormStatus
2024. 2. 12. 02:19ㆍReact
728x90
반응형
- form 액션의 결과에 기반하여 상태를 업데이트할 수 있게 해주는 Hook
- 마지막 양식 제출의 status 정보를 제공하는 Hook
📄 Docs
useFormState
💡 현재는 Canary와 experimental channel에서만 사용할 수 있다.
💡 useFormState을 사용하는 이점을 얻으려면, React 서버 컴포넌트를 지원하는 프레임워크에서 사용해야 한다.
- form 액션의 결과에 기반하여 상태를 업데이트할 수 있게 해주는 Hook이다.
const [state, formAction] = useFormState(fn, initialState, permalink?);
1. Reference
useFormState(action, initialState, permalink?)
- 컴포넌트 최상위 레벨에서 useFormState를 호출해서, form 액션이 호출될 때 업데이트되는 state를 생성한다.
- useFormState에 form의 액션 함수와 초기값을 전달하면, form에 사용할 새로운 액션과 최신 state를 반환한다.
- 최신 state는 제공한 함수에도 전달된다.
import { useFormState } from "react-dom";
async function increment(previousState, formData) {
return previousState + 1;
}
function StatefulForm({}) {
const [state, formAction] = useFormState(increment, 0);
return (
<form>
{state}
<button formAction={formAction}>Increment</button>
</form>
)
}
- form의 state는 form이 마지막으로 제출되었을 때 액션에 의해 반환된 값이다.
- form이 아직 제출되지 않았다면, 초기값을 가진다.
- 서버 액션을 사용하면 form을 제출하고 나서 서버의 응답을 hydration이 완료되기 전에 보여줄 수 있다.
1-1. Parameters
fn
- form이 제출될 때 호출할 함수
- 초기 인자로 form의 이전 state를 받는다.
- 그 다음에는 form 액션이 일반적으로 받는 인자들을 받는다.
initialState
- State의 초기값
- 처음 호출된 후에는 무시된다.
permalink (optional)
- form이 수정하는 고유 페이지 URL을 포함하는 문자열
- 동적 콘텐츠를 포함하는 페이지(ex. 피드)에서 점진적 개선을 위해 사용된다.
- 만약 fn이 서버 액션이고, JS 번들이 로드되기 전에 폼이 제출되면, 브라우저는 현재 페이지의 URL이 아닌 지정된 permalink URL로 이동한다.
- 당연히,, 이 페이지에서도 동일한 form 컴포넌트가 렌더링되도록 해놔야하고, 동일한 액션 fn 및 permalink도 포함시켜야 한다. 👉 React가 상태를 어떻게 전달해야 하는지 알 수 있게 하기 위함
- form이 hydrate되면, 이 매개변수는 더 이상 효과가 없다.
1-2. Returns
- 두 개의 값을 가지는 배열을 반환한다.
current state
- 액션에 의해 반환된 값
- 첫 렌더링에서는 전달한 initialState
new action
- form 컴포넌트의 액션 속성으로, 또는 form 내의 버튼 컴포넌트의 formAction 속성으로 전달할 수 있는 새로운 액션
1-3. 주의사항
1) 서버 컴포넌트와 사용
- React 서버 컴포넌트를 지원하는 프레임워크와 사용하면, 클라이언트에서 JS가 실행되기 전에 form을 interactive하게 만들 수 있다.
2) 서버 컴포넌트 없이 사용
- 컴포넌트의 로컬 state와 동일하다. (form의 상태가 컴포넌트 내에서만 유지되고 관리되며, form 상태와 관련된 로직은 클라이언트에서 JS가 로드되고 실행된 후에 처리된다.)
3) 함수 시그니처의 차이
- 전달된 함수는 첫번째 인자로 이전 state를 받는다. (기존 form 함수는 보통 이벤트 객체만 받음)
- 이는 함수가 useFormState 없이 직접 form 액션으로 사용될 때와 다른 시그니처(인자의 구성)을 갖게 만든다.
- 이로인해 특정 조건 하에만 State를 업데이트하는 등 세밀한 제어가 가능해진다.
2. 사용법
- useFormState에서 반환 받은 formAction은
<form>
의 action prop으로 전달된다.
import { useFormState } from 'react-dom';
import { action } from './actions.js';
function MyComponent() {
const [state, formAction] = useFormState(action, null);
// ...
return (
<form action={formAction}>
{/* ... */}
</form>
);
}
- form이 제출되면 인자로 전달한 action 함수가 호출되고, 이 함수의 반환 값이 form의 새로운 state가 된다.
- 제공한 액션은 첫 번째 인자로 currentState를 받고, 나머지 인자들은 useFormState를 사용하지 않았을 때와 동일하다.
function action(currentState, formData) {
// ...
return 'next state';
}
useFormStatus
💡 현재는 Canary와 experimental channel에서만 사용할 수 있다.
- 마지막 양식 제출의 status 정보를 제공하는 Hook이다.
const { pending, data, method, action } = useFormStatus();
1. Reference
useFormStatus()
- 아래 예제에서처럼 status 정보를 얻을(이 hook을 호출할) 컴포넌트가
<form>
내부에 렌더링되어야 한다.
import { useFormStatus } from "react-dom";
import action from './actions';
function Submit() {
const status = useFormStatus();
return <button disabled={status.pending}>Submit</button>
}
export default function App() {
return (
<form action={action}>
<Submit />
</form>
);
}
Parameters
- 없음
Returns
1) pending
- 제출중이면 true
- 아니면 false
2) data
- form이 제출하는 데이터를 포함하는 FormData 인터페이스를 구현한 객체
- 활성화된 제출이 없거나
<form>
부모가 없으면 null이 된다.
3) method
- get 또는 post (string)
- HTTP 메소드 중 어떤 것으로 제출되는지 알려준다.
- (기본적으로
<form>
은 GET 메소드를 사용하고, method 속성을 통해 지정할 수 있다.)
4) action
- 부모
<form>
의 action 속성에 전달된 함수에 대한 참조 - action 속성에 URI값이 제공되었거나 지정되지 않았으면 null이 된다.
2. 사용법
2-1. 대기 상태 표시하기
- pending 꺼내와서 분기
function Submit() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? "Submitting..." : "Submit"}
</button>
);
}
2-2. 제출된 form 데이터 읽기
- data 꺼내와서 읽기
- data를 활용해서 이미 제출한 데이터를 보여줄 수 있다. 예시 👇
import {useState, useMemo, useRef} from 'react';
import {useFormStatus} from 'react-dom';
export default function UsernameForm() {
const {pending, data} = useFormStatus();
const [showSubmitted, setShowSubmitted] = useState(false);
const submittedUsername = useRef(null);
const timeoutId = useRef(null);
useMemo(() => {
if (pending) {
submittedUsername.current = data?.get('username');
if (timeoutId.current != null) {
clearTimeout(timeoutId.current);
}
timeoutId.current = setTimeout(() => {
timeoutId.current = null;
setShowSubmitted(false);
}, 2000);
setShowSubmitted(true);
}
}, [pending, data]);
return (
<>
<label>Request a Username: </label><br />
<input type="text" name="username" />
<button type="submit" disabled={pending}>
{pending ? 'Submitting...' : 'Submit'}
</button>
{showSubmitted ? (
<p>Submitted request for username: {submittedUsername.current}</p>
) : null}
</>
);
}
728x90
반응형
'React' 카테고리의 다른 글
[React] createPortal (2) | 2024.03.05 |
---|---|
왜 Hooks는 컴포넌트의 최상위 레벨에서 호출해야 하나요? (1) | 2023.12.29 |
[React] useRef, forwardRef, useImperativeHandle (1) | 2023.12.12 |