React Server Components

'use server'React Server Components 用の機能です。

'use server' は、クライアントサイドのコードから呼び出せる、サーバサイドの関数をマークします。


リファレンス

'use server'

非同期 (async) 関数の本体の冒頭に 'use server'; を追加することで、その関数がクライアントから実行可能であるとマークします。そのような関数のことをサーバ関数 (Server Function) と呼びます。

async function addToCart(data) {
'use server';
// ...
}

クライアント上でサーバ関数を呼び出すと、渡された引数のシリアライズされたコピーを含んだネットワークリクエストがサーバに送信されます。サーバ関数が値を返す場合は、その値がシリアライズされてクライアントに返されます。

個々の関数に 'use server' をマークする代わりに、このディレクティブをファイルの先頭に追加することもできます。その場合はそのファイル内のすべてのエクスポートが、クライアントコードでインポートされる場合も含み、あらゆる場所で使用できるサーバアクションとしてマークされます。

注意点

  • 'use server' は、関数やモジュールの冒頭、つまりインポートも含む他のコードよりも上にある必要があります(ディレクティブの上にコメントを書くことは OK)。シングルクォートまたはダブルクォートで書かれていなければならず、バックティックは無効です。
  • 'use server' は、サーバサイドのファイルでのみ使用できます。結果として得られるサーバ関数は、props を通じてクライアントコンポーネントに渡せるようになります。サポートされているシリアライズ可能な型を参照してください。
  • クライアントコードからサーバ関数をインポートする場合は、ディレクティブをモジュールレベルで使用する必要があります。
  • 内部で使用されるネットワーク呼び出しは常に非同期であるため、'use server' は非同期関数でのみ使用できます。
  • サーバ関数への引数は常に信頼できない入力として扱い、あらゆるデータ書き換えを検証してください。セキュリティに関する考慮事項を参照してください。
  • サーバ関数はトランジションの中で呼び出すようにしてください。サーバ関数が <form action> または formAction に渡される場合、自動的にトランジション内で呼び出されます。
  • サーバ関数は、サーバ側の状態を書き換える、更新目的のために設計されています。データの取得には推奨されません。したがって、サーバ関数を実装するフレームワークは通常、一度にひとつのアクションのみを処理し、返り値をキャッシュしないようにします。

セキュリティについての考慮事項

サーバ関数への引数は、完全にクライアントで制御されるものです。セキュリティのため、入力は常に信頼できないものとして扱い、引数の検証やエスケープを適切に行ってください。

あらゆるサーバ関数において、ログイン済みのユーザにその処理の実行が許可されているのかを確認するようにしてください。

Under Construction

サーバ関数から機密データが送信されるのを防ぐために、ユニークな値やオブジェクトがクライアントコードに渡されるのを防ぐ実験的な汚染 API (taint API) があります。

experimental_taintUniqueValueexperimental_taintObjectReference を参照してください。

シリアライズ可能な引数と返り値

クライアントコードのサーバ関数呼び出しはネットワーク経由で行われるため、関数に渡すあらゆる引数はシリアライズ可能である必要があります。

以下は、サーバ関数の引数としてサポートされる型です。

以下のものはサポートされていません。

  • 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 する必要があります。