Перейти к содержанию

<form>

Canary

Расширения для <form> в React в настоящее время доступны только в канале React canary и экспериментальном канале. В стабильных релизах React <form> работает только как встроенный в браузер HTML-компонент. Подробнее о каналах выпуска React здесь.

Компонент встроенный в браузер <form> позволяет создавать интерактивные элементы управления для отправки информации.

1
2
3
4
<form action={search}>
    <input name="query" />
    <button type="submit">Search</button>
</form>

Описание

<form>

Чтобы создать интерактивные элементы управления для отправки информации, используйте встроенный компонент браузера <form>.

1
2
3
4
<form action={search}>
    <input name="query" />
    <button type="submit">Search</button>
</form>

Пропсы

<form> поддерживает все общие пропсы элементов

action: URL или функция. Когда в action передается URL, форма будет вести себя как компонент HTML-формы. Когда в action передается функция, она будет обрабатывать отправку формы. Функция, переданная в action, может быть асинхронной и будет вызвана с единственным аргументом, содержащим данные формы отправленной формы. Свойство action может быть переопределено атрибутом formAction компонента <button>, <input type="submit"> или <input type="image">.

Ограничения

  • Когда функция передается в action или formAction, метод HTTP будет POST, независимо от значения параметра method.

Использование

Обработка отправки формы на клиенте

Передайте функцию в свойство action формы для запуска функции при отправке формы. formData будет передан в функцию в качестве аргумента, чтобы вы могли получить доступ к данным, отправленным формой. Это отличается от обычного HTML action, который принимает только URL.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
export default function Search() {
    function search(formData) {
        const query = formData.get('query');
        alert(`You searched for '${query}'`);
    }
    return (
        <form action={search}>
            <input name="query" />
            <button type="submit">Search</button>
        </form>
    );
}

Обработка отправки формы с помощью серверного действия

Отображение <form> с вводом и кнопкой отправки. Передайте серверное действие (функцию, помеченную 'use server') в свойство action формы, чтобы запустить функцию при отправке формы.

Передача серверного действия в <form action> позволяет пользователям отправлять формы без включенного JavaScript или до загрузки кода. Это полезно для пользователей, у которых медленное соединение, устройство или отключен JavaScript, и похоже на то, как работают формы, когда в свойство action передается URL.

Вы можете использовать скрытые поля формы для предоставления данных действию <form>. Серверное действие будет вызвано с данными скрытого поля формы в виде экземпляра FormData.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import { updateCart } from './lib.js';

function AddToCart({ productId }) {
    async function addToCart(formData) {
        'use server';
        const productId = formData.get('productId');
        await updateCart(productId);
    }
    return (
        <form action={addToCart}>
            <input
                type="hidden"
                name="productId"
                value={productId}
            />
            <button type="submit">Add to Cart</button>
        </form>
    );
}

Вместо того чтобы использовать скрытые поля формы для передачи данных в действие <form>, вы можете вызвать метод bind, чтобы снабдить его дополнительными аргументами. Это приведет к привязке к функции нового аргумента (productId) в дополнение к formData, который передается в качестве аргумента функции.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import { updateCart } from './lib.js';

function AddToCart({ productId }) {
    async function addToCart(productId, formData) {
        'use server';
        await updateCart(productId);
    }
    const addProductToCart = addToCart.bind(
        null,
        productId
    );
    return (
        <form action={addProductToCart}>
            <button type="submit">Add to Cart</button>
        </form>
    );
}

Когда <form> отображается Серверным компонентом, а в параметр action формы <form> передается Серверное действие, форма прогрессивно улучшается.

Отображение состояния ожидания во время отправки формы

Чтобы отобразить состояние ожидания во время отправки формы, вы можете вызвать хук useFormStatus в компоненте, отображаемом в <form>, и прочитать возвращаемое свойство pending.

Здесь мы используем свойство pending, чтобы указать, что форма отправляется.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { useFormStatus } from 'react-dom';
import { submitForm } from './actions.js';

function Submit() {
    const { pending } = useFormStatus();
    return (
        <button type="submit" disabled={pending}>
            {pending ? 'Submitting...' : 'Submit'}
        </button>
    );
}

function Form({ action }) {
    return (
        <form action={action}>
            <Submit />
        </form>
    );
}

export default function App() {
    return <Form action={submitForm} />;
}

Подробнее о хуке useFormStatus читайте в справочной документации.

Оптимистическое обновление данных формы

Хук useOptimistic предоставляет возможность оптимистично обновлять пользовательский интерфейс до завершения фоновой операции, например, сетевого запроса. В контексте форм эта техника помогает сделать приложения более отзывчивыми. Когда пользователь отправляет форму, вместо того чтобы ждать, пока ответ сервера отразит изменения, интерфейс сразу же обновляется с ожидаемым результатом.

Например, когда пользователь вводит сообщение в форму и нажимает кнопку "Отправить", хук useOptimistic позволяет сообщению сразу же появиться в списке с надписью "Отправка...", еще до того, как оно будет отправлено на сервер. Такой "оптимистичный" подход создает впечатление скорости и оперативности. Затем форма пытается действительно отправить сообщение в фоновом режиме. Как только сервер подтверждает, что сообщение получено, метка "Отправка..." удаляется.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import { useOptimistic, useState, useRef } from 'react';
import { deliverMessage } from './actions.js';

function Thread({ messages, sendMessage }) {
    const formRef = useRef();
    async function formAction(formData) {
        addOptimisticMessage(formData.get('message'));
        formRef.current.reset();
        await sendMessage(formData);
    }
    const [
        optimisticMessages,
        addOptimisticMessage,
    ] = useOptimistic(messages, (state, newMessage) => [
        ...state,
        {
            text: newMessage,
            sending: true,
        },
    ]);

    return (
        <>
            {optimisticMessages.map((message, index) => (
                <div key={index}>
                    {message.text}
                    {!!message.sending && (
                        <small> (Sending...)</small>
                    )}
                </div>
            ))}
            <form action={formAction} ref={formRef}>
                <input
                    type="text"
                    name="message"
                    placeholder="Hello!"
                />
                <button type="submit">Send</button>
            </form>
        </>
    );
}

export default function App() {
    const [messages, setMessages] = useState([
        { text: 'Hello there!', sending: false, key: 1 },
    ]);
    async function sendMessage(formData) {
        const sentMessage = await deliverMessage(
            formData.get('message')
        );
        setMessages([...messages, { text: sentMessage }]);
    }
    return (
        <Thread
            messages={messages}
            sendMessage={sendMessage}
        />
    );
}
1
2
3
4
export async function deliverMessage(message) {
    await new Promise((res) => setTimeout(res, 1000));
    return message;
}

Обработка ошибок отправки формы

В некоторых случаях функция, вызываемая реквизитом <form> action, выбрасывает ошибку. Вы можете обработать эти ошибки, обернув <form> в границу ошибки. Если функция, вызываемая реквизитом <form> action, выдает ошибку, то будет отображена обратная связь для границы ошибки.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { ErrorBoundary } from 'react-error-boundary';

export default function Search() {
    function search() {
        throw new Error('search error');
    }
    return (
        <ErrorBoundary
            fallback={
                <p>
                    There was an error while submitting the
                    form
                </p>
            }
        >
            <form action={search}>
                <input name="query" />
                <button type="submit">Search</button>
            </form>
        </ErrorBoundary>
    );
}

Отображение ошибки отправки формы без JavaScript

Отображение сообщения об ошибке отправки формы до загрузки пакета JavaScript для прогрессивного улучшения требует, чтобы:

  1. <form> должна быть отображена серверным компонентом
  2. функция, передаваемая в свойство action <form>, должна быть Server Action
  3. useActionState Hook будет использоваться для отображения сообщения об ошибке

useActionState принимает два параметра: Server Action и начальное состояние. useActionState возвращает два значения: переменную состояния и экшен. Действие, возвращаемое useActionState, должно быть передано в свойство action формы. Переменная состояния, возвращаемая useActionState, может быть использована для отображения сообщения об ошибке. Значение, возвращаемое Server Action, переданное в useActionState, будет использовано для обновления переменной состояния.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import { useActionState } from 'react';
import { signUpNewUser } from './api';

export default function Page() {
    async function signup(prevState, formData) {
        'use server';
        const email = formData.get('email');
        try {
            await signUpNewUser(email);
            alert(`Added "${email}"`);
        } catch (err) {
            return err.toString();
        }
    }
    const [message, formAction] = useActionState(
        signup,
        null
    );
    return (
        <>
            <h1>Signup for my newsletter</h1>
            <p>
                Signup with the same email twice to see an
                error
            </p>
            <form action={formAction} id="signup-form">
                <label htmlFor="email">Email: </label>
                <input
                    name="email"
                    id="email"
                    placeholder="[email protected]"
                />
                <button>Sign up</button>
                {!!message && <p>{message}</p>}
            </form>
        </>
    );
}

Подробнее об обновлении состояния из действия формы можно узнать из документации useActionState

Обработка нескольких типов отправки

Формы могут быть разработаны для обработки нескольких действий отправки, основанных на кнопке, нажатой пользователем. Каждая кнопка внутри формы может быть связана с определенным действием или поведением путем установки свойства formAction.

Когда пользователь нажимает определенную кнопку, форма отправляется, и выполняется соответствующее действие, определенное атрибутами и действием этой кнопки. Например, форма может по умолчанию отправлять статью на проверку, но иметь отдельную кнопку с formAction, установленную для сохранения статьи в черновик.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
export default function Search() {
    function publish(formData) {
        const content = formData.get('content');
        const button = formData.get('button');
        alert(
            `'${content}' was published with the '${button}' button`
        );
    }

    function save(formData) {
        const content = formData.get('content');
        alert(`Your draft of '${content}' has been saved!`);
    }

    return (
        <form action={publish}>
            <textarea name="content" rows={4} cols={40} />
            <br />
            <button
                type="submit"
                name="button"
                value="submit"
            >
                Publish
            </button>
            <button formAction={save}>Save draft</button>
        </form>
    );
}

Источник — https://react.dev/reference/react-dom/components/form

Комментарии