Open keitakn opened 1 month ago
フォームで画像を送信する際は useActionState を利用する。
しかしNext.js v14 + React v18.3だと利用出来ないので以下のようにする事で useActionState
を利用する。
公式の useActionState
が利用可能になったらそちらに置き換えればOK。
src/app/test/_types/FormState.ts
export type FormState = { errors: { word?: string }, sendWord?: string };
src/app/test/_actions/myAction.ts
'use server';
import type { FormState } from '@/app/test/_types/FormState';
import { setTimeout } from 'timers/promises';
export const myAction = async (state: FormState, payload: FormData) => {
const newState = structuredClone(state);
await setTimeout(1000);
if (!payload.get('word')) {
return {
errors: {
word: '入力してください',
},
};
}
newState.sendWord = payload.get('word') as string;
newState.errors.word = '';
return newState;
};
src/app/test/_actions/useActionState.ts
'use client';
import { useCallback, useTransition } from 'react';
import { useFormState } from 'react-dom';
/**
* @see {@link https://github.com/DefinitelyTyped/DefinitelyTyped/blob/0b728411cd1dfb4bd26992bb35a73cf8edaa22e7/types/react/canary.d.ts#L103-L112}
*/
export function useActionState<State>(
action: (state: Awaited<State>) => State | Promise<State>,
initialState: Awaited<State>,
permalink?: string,
): [state: Awaited<State>, dispatch: () => void, isPending: boolean];
export function useActionState<State, Payload>(
action: (state: Awaited<State>, payload: Payload) => State | Promise<State>,
initialState: Awaited<State>,
permalink?: string,
): [state: Awaited<State>, dispatch: (payload: Payload) => void, isPending: boolean];
export function useActionState<State, Payload>(
action: (state: Awaited<State>, payload: Payload) => State | Promise<State>,
initialState: Awaited<State>,
permalink?: string,
) {
const [isPending, startTransition] = useTransition();
const [currentState, dispatchAction] = useFormState(action, initialState, permalink);
const finalAction = useCallback(
(payload: Payload) => {
startTransition(() => {
dispatchAction(payload);
});
},
[dispatchAction],
);
return [currentState, finalAction, isPending];
}
src/app/test/_components/MyForm.tsx
'use client';
import { useActionState } from '../_actions/useActionState';
import { myAction } from '../_actions/myAction';
export const MyForm = () => {
const [currentState, action, isPending] = useActionState(myAction, { errors: {} });
return (
<form action={action}>
<div>
<input type="text" name="word" />
{currentState.errors.word && <div>{currentState.errors.word}</div>}
</div>
<button disabled={isPending}>Submit</button>
{isPending && <div>送信中...</div>}
{currentState.sendWord && <div>{currentState.sendWord} が送信されました!</div>}
</form>
);
};
src/app/test/page.tsx
import { MyForm } from '@/app/test/_components/MyForm';
const TestPage = () => {
return (
<div>
<h1>Test Page</h1>
<MyForm />
</div>
);
};
export default TestPage;
Done の定義
補足情報
https://www.figma.com/design/zkmgb1HoYkaMwitEVaHQyF/LGTMeow-UI-2024?node-id=0-1&m=dev