| title | useActionState |
|---|
useActionState — это хук, который позволяет обновлять состояние на основе результата действия формы.
const [state, formAction, isPending] = useActionState(fn, initialState, permalink?);В ранних версиях React Canary этот API был частью React DOM и назывался useFormState.
{/* TODO T164397693: link to actions documentation once it exists */}
Вызовите useActionState на верхнем уровне вашего компонента, чтобы создать состояние компонента, которое обновляется при вызове действия формы. Вы передаёте useActionState существующую функцию действия формы, а также начальное состояние, и он возвращает новое действие, которое вы используете в своей форме, вместе с последним состоянием формы и индикатором того, выполняется ли действие в данный момент. Последнее состояние формы также передается в функцию, которую вы предоставили.
import { useActionState } from "react";
async function increment(previousState, formData) {
return previousState + 1;
}
function StatefulForm({}) {
const [state, formAction] = useActionState(increment, 0);
return (
<form>
{state}
<button formAction={formAction}>Increment</button>
</form>
)
}Состояние формы — это значение, возвращаемое действием при последней отправке формы. Если форма еще не была отправлена, это начальное состояние, которое вы передали.
При использовании с серверной функцией useActionState позволяет отображать ответ сервера после отправки формы еще до завершения гидратации.
fn: Функция, которая будет вызвана при отправке формы или нажатии кнопки. Когда функция будет вызвана, она получит предыдущее состояние формы (изначальноinitialState, которое вы передали, впоследствии — его предыдущее возвращаемое значение) в качестве первого аргумента, за которым следуют аргументы, которые обычно получает действие формы.initialState: Значение, которое вы хотите установить для состояния изначально. Это может быть любое сериализуемое значение. Этот аргумент игнорируется после первого вызова действия.- необязательный
permalink: Строка, содержащая уникальный URL страницы, которую изменяет эта форма. Используется на страницах с динамическим контентом (например, лентами) в сочетании с прогрессивным улучшением: еслиfnявляется серверной функцией, и форма отправляется до загрузки JavaScript-пакета, браузер перейдет по указанному URLpermalinkвместо URL текущей страницы. Убедитесь, что тот же компонент формы отображается на целевой странице (включая то же действиеfnиpermalink), чтобы React знал, как передать состояние. Как только форма будет гидратирована, этот параметр не будет иметь никакого эффекта.
{/* TODO T164397693: link to serializable values docs once it exists */}
useActionState возвращает массив со следующими значениями:
- Текущее состояние. Во время первого рендера оно будет соответствовать переданному вами
initialState. После вызова действия оно будет соответствовать значению, возвращенному действием. - Новое действие, которое вы можете передать в качестве
actionдля вашего компонентаformилиformActionдля любого компонентаbuttonвнутри формы. Действие также можно вызвать вручную вstartTransition. - Флаг
isPending, который указывает, есть ли ожидающий переход.
- При использовании с фреймворком, поддерживающим React Server Components,
useActionStateпозволяет сделать формы интерактивными до выполнения JavaScript на клиенте. При использовании без Server Components он эквивалентен локальному состоянию компонента. - Функция, переданная в
useActionState, получает дополнительный аргумент — предыдущее или начальное состояние — в качестве первого аргумента. Это делает ее сигнатуру отличной от той, если бы она использовалась напрямую как действие формы без использованияuseActionState.
Использование информации, возвращаемой действием формы {/using-information-returned-by-a-form-action/}
Вызовите useActionState на верхнем уровне вашего компонента, чтобы получить доступ к возвращаемому значению действия с момента последней отправки формы.
import { useActionState } from 'react';
import { action } from './actions.js';
function MyComponent() {
const [state, formAction] = useActionState(action, null);
// ...
return (
<form action={formAction}>
{/* ... */}
</form>
);
}useActionState возвращает массив со следующими элементами:
- Текущее состояние формы, которое изначально устанавливается в начальное состояние, которое вы предоставили, а после отправки формы устанавливается в возвращаемое значение действия, которое вы предоставили.
- Новое действие, которое вы передаете в
<form>как его свойствоactionили вызываете вручную вstartTransition. - Флаг ожидания (
isPending), который вы можете использовать, пока ваше действие обрабатывается.
Когда форма отправляется, будет вызвана функция действия, которую вы предоставили. Ее возвращаемое значение станет новым текущим состоянием формы.
Действие, которое вы предоставляете, также получит новый первый аргумент, а именно текущее состояние формы. При первой отправке формы это будет начальное состояние, которое вы предоставили, а при последующих отправках — возвращаемое значение из последнего вызова действия. Остальные аргументы такие же, как если бы useActionState не использовался.
function action(currentState, formData) {
// ...
return 'next state';
}Чтобы отображать сообщения, такие как сообщение об ошибке или уведомление, возвращаемое серверной функцией, оберните действие в вызов useActionState.
import { useActionState, useState } from "react";
import { addToCart } from "./actions.js";
function AddToCartForm({itemID, itemTitle}) {
const [message, formAction, isPending] = useActionState(addToCart, null);
return (
<form action={formAction}>
<h2>{itemTitle}</h2>
<input type="hidden" name="itemID" value={itemID} />
<button type="submit">Add to Cart</button>
{isPending ? "Loading..." : message}
</form>
);
}
export default function App() {
return (
<>
<AddToCartForm itemID="1" itemTitle="JavaScript: The Definitive Guide" />
<AddToCartForm itemID="2" itemTitle="JavaScript: The Good Parts" />
</>
)
}"use server";
export async function addToCart(prevState, queryData) {
const itemID = queryData.get('itemID');
if (itemID === "1") {
return "Added to cart";
} else {
// Add a fake delay to make waiting noticeable.
await new Promise(resolve => {
setTimeout(resolve, 2000);
});
return "Couldn't add to cart: the item is sold out.";
}
}form {
border: solid 1px black;
margin-bottom: 24px;
padding: 12px
}
form button {
margin-right: 12px;
}Отображение структурированной информации после отправки формы {/display-structured-information-after-submitting-a-form/}
Возвращаемое значение серверной функции может быть любым сериализуемым значением. Например, это может быть объект, включающий булево значение, указывающее на успешность действия, сообщение об ошибке или обновленную информацию.
import { useActionState, useState } from "react";
import { addToCart } from "./actions.js";
function AddToCartForm({itemID, itemTitle}) {
const [formState, formAction] = useActionState(addToCart, {});
return (
<form action={formAction}>
<h2>{itemTitle}</h2>
<input type="hidden" name="itemID" value={itemID} />
<button type="submit">Add to Cart</button>
{formState?.success &&
<div className="toast">
Added to cart! Your cart now has {formState.cartSize} items.
</div>
}
{formState?.success === false &&
<div className="error">
Failed to add to cart: {formState.message}
</div>
}
</form>
);
}
export default function App() {
return (
<>
<AddToCartForm itemID="1" itemTitle="JavaScript: The Definitive Guide" />
<AddToCartForm itemID="2" itemTitle="JavaScript: The Good Parts" />
</>
)
}"use server";
export async function addToCart(prevState, queryData) {
const itemID = queryData.get('itemID');
if (itemID === "1") {
return {
success: true,
cartSize: 12,
};
} else {
return {
success: false,
message: "The item is sold out.",
};
}
}form {
border: solid 1px black;
margin-bottom: 24px;
padding: 12px
}
form button {
margin-right: 12px;
}Мое действие больше не может читать отправленные данные формы {/my-action-can-no-longer-read-the-submitted-form-data/}
Когда вы оборачиваете действие с помощью useActionState, оно получает дополнительный аргумент в качестве первого аргумента. Поэтому отправленные данные формы становятся его вторым аргументом вместо первого, как это обычно бывает. Новый первый аргумент, который добавляется, — это текущее состояние формы.
function action(currentState, formData) {
// ...
}