'use server'
'use server'
は、クライアントサイドのコードから呼び出せる、サーバサイドの関数をマークします。
リファレンス
'use server'
非同期 (async) 関数の本体の冒頭に 'use server';
を追加することで、その関数がクライアントから実行可能であるとマークします。そのような関数のことをサーバアクション (Server Action) と呼びます。
async function addToCart(data) {
'use server';
// ...
}
クライアント上でサーバアクションを呼び出すと、渡された引数のシリアライズされたコピーを含んだネットワークリクエストがサーバに送信されます。サーバアクションが値を返す場合は、その値がシリアライズされてクライアントに返されます。
個々の関数に 'use server'
をマークする代わりに、このディレクティブをファイルの先頭に追加することもできます。その場合はそのファイル内のすべてのエクスポートが、クライアントコードでインポートされる場合も含み、あらゆる場所で使用できるサーバアクションとしてマークされます。
注意点
'use server'
は、関数やモジュールの冒頭、つまりインポートも含む他のコードよりも上にある必要があります(ディレクティブの上にコメントを書くことは OK)。シングルクォートまたはダブルクォートで書かれていなければならず、バックティックは無効です。'use server'
は、サーバサイドのファイルでのみ使用できます。結果として得られるサーバアクションは、props を通じてクライアントコンポーネントに渡せるようになります。サポートされているシリアライズ可能な型を参照してください。- クライアントコードからサーバアクションをインポートする場合は、ディレクティブをモジュールレベルで使用する必要があります。
- 内部で使用されるネットワーク呼び出しは常に非同期であるため、
'use server'
は非同期関数でのみ使用できます。 - サーバアクションへの引数は常に信頼できない入力として扱い、あらゆるデータ書き換えを検証してください。セキュリティに関する考慮事項を参照してください。
- サーバアクションはトランジションの中で呼び出すようにしてください。サーバアクションが
<form action>
またはformAction
に渡される場合、自動的にトランジション内で呼び出されます。 - サーバアクションは、サーバ側の状態を書き換える、更新目的のために設計されています。データの取得には推奨されません。したがって、サーバアクションを実装するフレームワークは通常、一度にひとつのアクションのみを処理し、返り値をキャッシュしないようにします。
セキュリティについての考慮事項
サーバアクションへの引数は、完全にクライアントで制御されるものです。セキュリティのため、入力は常に信頼できないものとして扱い、引数の検証やエスケープを適切に行ってください。
あらゆるサーバアクションにおいて、ログイン済みのユーザがそのアクションを実行できることを確認してください。
シリアライズ可能な引数と返り値
クライアントコードのサーバアクション呼び出しはネットワーク経由で行われるため、関数に渡すあらゆる引数はシリアライズ可能である必要があります。
以下は、サーバアクションの引数としてサポートされる型です。
- プリミティブ
- シリアライズ可能な値を含んだ Iterable
- Date
- FormData のインスタンス
- プレーンなオブジェクト: オブジェクト初期化子で作成され、シリアライズ可能なプロパティを持つもの
- サーバアクション (server action) としての関数
- プロミス
以下のものはサポートされていません。
- React 要素すなわち JSX
- 関数。関数コンポーネントや、サーバアクションでない他のあらゆる関数を含む。
- クラス
- 任意のクラスのインスタンス(上記の組み込みクラスを除く)や、null プロトタイプのオブジェクト
- グローバルに登録されていないシンボル、例:
Symbol('my new symbol')
サポートされるシリアライズ可能な返り値は、クライアントコンポーネントに渡せるシリアライズ可能な props の型と同じです。
使用法
フォームでサーバアクションを使用する
サーバアクションの最も一般的なユースケースは、データを更新するためにサーバ関数を呼び出すことです。ブラウザ上においては HTML フォーム要素が、ユーザが更新処理を送信する際の伝統的な方法です。React Server Components により、フォームに書かれたサーバアクションに対する第 1 級サポートが導入されます。
以下は、ユーザがユーザ名をリクエストできるフォームです。
// App.js
async function requestUsername(formData) {
'use server';
const username = formData.get('username');
// ...
}
export default function App() {
return (
<form action={requestUsername}>
<input type="text" name="username" />
<button type="submit">Request</button>
</form>
);
}
この例では、requestUsername
は <form>
に渡されるサーバアクションとなります。ユーザがこのフォームを送信すると、サーバ関数 requestUsername
へのネットワークリクエストが発生します。フォーム内でサーバアクションを呼び出すとき、React はフォームの FormData をサーバアクションの最初の引数として提供します。
フォームの action
にサーバアクションを渡すことで、React によるフォームのプログレッシブエンハンスメント (progressive enhancement) が有効になります。つまり JavaScript バンドルがロードされる前にフォームを送信できるようになるということです。
フォームでの返り値の取り扱い
上記のユーザ名リクエストフォームでは、ユーザ名が利用できない可能性もあります。requestUsername
は成功したか失敗したかを伝えられるべきです。
プログレッシブエンハンスメントをサポートしつつサーバアクションの結果に基づいて UI を更新するには、useActionState
を使用します。
// requestUsername.js
'use server';
export default async function requestUsername(formData) {
const username = formData.get('username');
if (canRequest(username)) {
// ...
return 'successful';
}
return 'failed';
}
// UsernameForm.js
'use client';
import { useActionState } from 'react';
import requestUsername from './requestUsername';
function UsernameForm() {
const [state, action] = useActionState(requestUsername, null, 'n/a');
return (
<>
<form action={action}>
<input type="text" name="username" />
<button type="submit">Request</button>
</form>
<p>Last submission request returned: {state}</p>
</>
);
}
ほとんどのフックと同様に、useActionState
はクライアントコード内でしか呼び出せないことに注意してください。
<form>
の外部でサーバアクションを呼び出す
サーバアクションはサーバ側の公開エンドポイントであり、クライアントコードのどこからでも呼び出すことができます。
フォームの外部でサーバアクションを使用する場合、トランジション内でサーバアクションを呼び出すようにしてください。これによりローディングインジケータを表示したり、楽観的に state 更新結果を表示したり、予期せぬエラーを処理したりすることができるようになります。フォームではサーバアクションは自動的にトランジション内にラップされます。
import incrementLike from './actions';
import { useState, useTransition } from 'react';
function LikeButton() {
const [isPending, startTransition] = useTransition();
const [likeCount, setLikeCount] = useState(0);
const onClick = () => {
startTransition(async () => {
const currentCount = await incrementLike();
setLikeCount(currentCount);
});
};
return (
<>
<p>Total Likes: {likeCount}</p>
<button onClick={onClick} disabled={isPending}>Like</button>;
</>
);
}
// actions.js
'use server';
let likeCount = 0;
export default async function incrementLike() {
likeCount++;
return likeCount;
}
サーバアクションからの返り値を読み取るには、返されたプロミスを await
する必要があります。