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

<input>

Встроенный компонент браузера <input> позволяет отображать различные виды вводимых форм.

1
<input />

Описание

<input>

Чтобы отобразить ввод, отобразите компонент встроенный в браузер <input>.

1
<input name="myInput" />

Свойства

input поддерживает все общие пропсы элементов.

Вы можете сделать input управляемым, передав один из этих пропсов:

  • checked: Булево значение. Для входа с чекбоксом или радиокнопкой контролирует, выбран ли он.
  • value: Строка. Для текстового ввода управляет его текстом. (Для радиокнопки определяет данные формы).

При передаче любого из них вы также должны передать обработчик onChange, который обновляет переданное значение.

Эти пропсы input актуальны только для неконтролируемых входов:

  • defaultChecked: Булево значение. Определяет начальное значение для входов type="checkbox" и type="radio".
  • defaultValue: Строка. Определяет начальное значение для текстового ввода.

Эти пропсы input актуальны как для неконтролируемых, так и для контролируемых входов:

  • accept: Строка. Определяет, какие типы файлов принимаются входом type="file".
  • alt: Строка. Определяет альтернативный текст изображения для входа type="image".
  • capture: Строка. Указывает носитель (микрофон, видео или камера), захваченный входом type="file".
  • autoComplete: Строка. Определяет один из возможных вариантов поведения автозаполнения.
  • autoFocus: Булево значение. Если true, React будет фокусировать элемент на монтировании.
  • dirname: Строка. Указывает имя поля формы для направленности элемента.
  • disabled: Булево значение. Если true, вход не будет интерактивным и будет отображаться затемненным.
  • children: input не принимает детей.
  • form: Строка. Указывает id формы, к которой принадлежит этот input. Если опущено, то это ближайшая родительская форма.
  • formAction: Строка. Переопределяет родительское <form action> для type="submit" и type="image".
  • formEnctype: Строка. Переопределяет родительский <form enctype> для type="submit" и type="image".
  • formMethod: Строка. Переопределяет родительский <метод формы> для type="submit" и type="image".
  • formNoValidate: Строка. Переопределяет родительскую <form noValidate> для type="submit" и type="image".
  • formTarget: Строка. Переопределяет родительскую <form target> для type="submit" и type="image".
  • height: Строка. Определяет высоту изображения для type="image".
  • list: Строка. Указывает id <datalist> с опциями автозаполнения.
  • max: Число. Определяет максимальное значение числовых данных и данных времени.
  • maxLength: Число. Определяет максимальную длину текстовых и других вводов.

Предостережения

  • Для флажков нужно checked (или defaultChecked), а не value (или defaultValue).
  • Если текстовый ввод получает строковое значение value, он будет рассматриваться как управляемый.
  • Если чекбокс или радиокнопка получает булево свойство checked, они будут рассматриваться как управляемые.
  • Вход не может быть одновременно управляемым и неуправляемым.
  • Вход не может переключаться между управляемым и неуправляемым в течение своего существования.
  • Каждый управляемый вход нуждается в обработчике события onChange, который синхронно обновляет его базовое значение.

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

Отображение входов различных типов

Чтобы отобразить ввод, создайте компонент input. По умолчанию это будет текстовый ввод. Вы можете передать type="checkbox" для флажка, type="radio" для радиокнопки, или один из других типов ввода.

 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
export default function MyForm() {
    return (
        <>
            <label>
                Text input: <input name="myInput" />
            </label>
            <hr />
            <label>
                Checkbox:{' '}
                <input type="checkbox" name="myCheckbox" />
            </label>
            <hr />
            <p>
                Radio buttons:
                <label>
                    <input
                        type="radio"
                        name="myRadio"
                        value="option1"
                    />
                    Option 1
                </label>
                <label>
                    <input
                        type="radio"
                        name="myRadio"
                        value="option2"
                    />
                    Option 2
                </label>
                <label>
                    <input
                        type="radio"
                        name="myRadio"
                        value="option3"
                    />
                    Option 3
                </label>
            </p>
        </>
    );
}

Предоставление метки для ввода {/providing-a-label-for-an-input/}

Обычно вы помещаете каждый input в тег label. Это сообщает браузеру, что данная метка связана с этим входом. Когда пользователь нажимает на метку, браузер автоматически фокусируется на вводе. Это также необходимо для обеспечения доступности: программа чтения с экрана объявит надпись на ярлыке, когда пользователь сфокусируется на связанном вводе.

Если вы не можете вложить input в label, свяжите их, передав одинаковый ID в <input id> и <label htmlFor>. Чтобы избежать конфликтов между несколькими экземплярами одного компонента, создайте такой ID с помощью useId.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import { useId } from 'react';

export default function Form() {
    const ageInputId = useId();
    return (
        <>
            <label>
                Your first name:
                <input name="firstName" />
            </label>
            <hr />
            <label htmlFor={ageInputId}>Your age:</label>
            <input
                id={ageInputId}
                name="age"
                type="number"
            />
        </>
    );
}

Предоставление начального значения для входа

Вы можете опционально указать начальное значение для любого входа. Для текстовых входов передавайте его как строку defaultValue. Для флажков и радиокнопок начальное значение должно задаваться булевым значением defaultChecked.

 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
export default function MyForm() {
    return (
        <>
            <label>
                Text input:{' '}
                <input
                    name="myInput"
                    defaultValue="Some initial value"
                />
            </label>
            <hr />
            <label>
                Checkbox:{' '}
                <input
                    type="checkbox"
                    name="myCheckbox"
                    defaultChecked={true}
                />
            </label>
            <hr />
            <p>
                Radio buttons:
                <label>
                    <input
                        type="radio"
                        name="myRadio"
                        value="option1"
                    />
                    Option 1
                </label>
                <label>
                    <input
                        type="radio"
                        name="myRadio"
                        value="option2"
                        defaultChecked={true}
                    />
                    Option 2
                </label>
                <label>
                    <input
                        type="radio"
                        name="myRadio"
                        value="option3"
                    />
                    Option 3
                </label>
            </p>
        </>
    );
}

Чтение значений ввода при отправке формы

Добавьте <form> вокруг ваших входных данных с <button type="submit"> внутри. Это вызовет обработчик события <form onSubmit>. По умолчанию браузер отправит данные формы на текущий URL и обновит страницу. Вы можете отменить это поведение, вызвав e.preventDefault(). Считайте данные формы с помощью new FormData(e.target).

 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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
export default function MyForm() {
    function handleSubmit(e) {
        // Prevent the browser from reloading the page
        e.preventDefault();

        // Read the form data
        const form = e.target;
        const formData = new FormData(form);

        // You can pass formData as a fetch body directly:
        fetch('/some-api', {
            method: form.method,
            body: formData,
        });

        // Or you can work with it as a plain object:
        const formJson = Object.fromEntries(
            formData.entries()
        );
        console.log(formJson);
    }

    return (
        <form method="post" onSubmit={handleSubmit}>
            <label>
                Text input:{' '}
                <input
                    name="myInput"
                    defaultValue="Some initial value"
                />
            </label>
            <hr />
            <label>
                Checkbox:{' '}
                <input
                    type="checkbox"
                    name="myCheckbox"
                    defaultChecked={true}
                />
            </label>
            <hr />
            <p>
                Radio buttons:
                <label>
                    <input
                        type="radio"
                        name="myRadio"
                        value="option1"
                    />{' '}
                    Option 1
                </label>
                <label>
                    <input
                        type="radio"
                        name="myRadio"
                        value="option2"
                        defaultChecked={true}
                    />{' '}
                    Option 2
                </label>
                <label>
                    <input
                        type="radio"
                        name="myRadio"
                        value="option3"
                    />{' '}
                    Option 3
                </label>
            </p>
            <hr />
            <button type="reset">Reset form</button>
            <button type="submit">Submit form</button>
        </form>
    );
}

Дайте name каждому <input>, например <input name="firstName" defaultValue="Taylor" />. Указанное вами name будет использоваться в качестве ключа в данных формы, например { firstName: "Taylor" }.

По умолчанию любая <кнопка> внутри <формы> отправит ее. Это может быть неожиданно! Если у вас есть собственный пользовательский компонент React Button, подумайте о возврате <button type="button"> вместо <button>. Затем, чтобы быть однозначным, используйте <button type="submit"> для кнопок, которые должны отправлять форму.

Управление input с помощью переменной состояния

Input типа <input /> является неуправляемым. Даже если вы передаете начальное значение, например <input defaultValue="Initial text" />, ваш JSX определяет только начальное значение. Он не контролирует, каким должно быть значение в данный момент.

Чтобы отобразить управляемый вход, передайте ему свойство value (или checked для чекбоксов и радио). React заставит вход всегда иметь переданное вами value. Обычно для этого объявляется переменная состояния:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function Form() {
    const [firstName, setFirstName] = useState(''); // Declare a state variable...
    // ...
    return (
        <input
            value={firstName} // ...force the input's value to match the state variable...
            onChange={(e) => setFirstName(e.target.value)} // ... and update the state variable on any edits!
        />
    );
}

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
function Form() {
    const [firstName, setFirstName] = useState('');
    return (
        <>
            <label>
                First name:
                <input
                    value={firstName}
                    onChange={(e) =>
                        setFirstName(e.target.value)
                    }
                />
            </label>
            {firstName !== '' && (
                <p>Your name is {firstName}.</p>
            )}
            ...
        </>
    );
}

Это также полезно, если вы хотите предложить несколько способов изменения состояния ввода (например, нажатием кнопки):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
function Form() {
    // ...
    const [age, setAge] = useState('');
    const ageAsNumber = Number(age);
    return (
        <>
            <label>
                Age:
                <input
                    value={age}
                    onChange={(e) => setAge(e.target.value)}
                    type="number"
                />
                <button
                    onClick={() => setAge(ageAsNumber + 10)}
                >
                    Add 10 years
                </button>
            </label>
        </>
    );
}

Значение value, которое вы передаете управляемым компонентам, не должно быть undefined или null. Если вам нужно, чтобы начальное значение было пустым (как в случае с полем firstName ниже), инициализируйте переменную состояния пустой строкой ('').

 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
import { useState } from 'react';

export default function Form() {
    const [firstName, setFirstName] = useState('');
    const [age, setAge] = useState('20');
    const ageAsNumber = Number(age);
    return (
        <>
            <label>
                First name:
                <input
                    value={firstName}
                    onChange={(e) =>
                        setFirstName(e.target.value)
                    }
                />
            </label>
            <label>
                Age:
                <input
                    value={age}
                    onChange={(e) => setAge(e.target.value)}
                    type="number"
                />
                <button
                    onClick={() => setAge(ageAsNumber + 10)}
                >
                    Add 10 years
                </button>
            </label>
            {firstName !== '' && (
                <p>Your name is {firstName}.</p>
            )}
            {ageAsNumber > 0 && (
                <p>Your age is {ageAsNumber}.</p>
            )}
        </>
    );
}

Если вы передадите value без onChange, то ввод будет невозможен. Когда вы управляете вводом, передавая ему некоторое value, вы принуждаете его всегда иметь то значение, которое вы передали. Поэтому если вы передадите переменную состояния в качестве value, но забудете синхронно обновить эту переменную состояния в обработчике события onChange, React будет возвращать ввод после каждого нажатия клавиши к указанному вами value.

Оптимизация повторного рендеринга при каждом нажатии клавиши

Когда вы используете управляемый ввод, вы устанавливаете состояние при каждом нажатии клавиши. Если компонент, содержащий ваше состояние, перерисовывает большое дерево, это может стать медленным. Есть несколько способов оптимизировать производительность повторного рендеринга.

Например, предположим, вы начинаете с формы, которая при каждом нажатии клавиши перерисовывает все содержимое страницы:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
function App() {
    const [firstName, setFirstName] = useState('');
    return (
        <>
            <form>
                <input
                    value={firstName}
                    onChange={(e) =>
                        setFirstName(e.target.value)
                    }
                />
            </form>
            <PageContent />
        </>
    );
}

Поскольку <PageContent /> не зависит от состояния ввода, вы можете перенести состояние ввода в свой собственный компонент:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
function App() {
    return (
        <>
            <SignupForm />
            <PageContent />
        </>
    );
}

function SignupForm() {
    const [firstName, setFirstName] = useState('');
    return (
        <form>
            <input
                value={firstName}
                onChange={(e) =>
                    setFirstName(e.target.value)
                }
            />
        </form>
    );
}

Это значительно повышает производительность, поскольку теперь только SignupForm рендерится при каждом нажатии клавиши.

Если нет возможности избежать повторного рендеринга (например, если PageContent зависит от значения поискового ввода), useDeferredValue позволяет сохранить отзывчивость управляемого ввода даже в середине большого повторного рендеринга.

Устранение неполадок

Мой текстовый ввод не обновляется, когда я ввожу текст

Если вы отображаете ввод с value, но без onChange, вы увидите ошибку в консоли:

1
2
// 🔴 Bug: controlled text input with no onChange handler
<input value={something} />

Ошибка

Вы предоставили свойство value полю формы без обработчика onChange. Это приведет к тому, что поле будет доступно только для чтения. Если поле должно быть изменяемым, используйте defaultValue. В противном случае установите либо onChange, либо readOnly.

Как следует из сообщения об ошибке, если вы хотите указать только начальное значение, передайте defaultValue вместо этого:

1
2
// ✅ Good: uncontrolled input with an initial value
<input defaultValue={something} />

Если вы хотите управлять этим входом с помощью переменной состояния, укажите обработчик onChange:

1
2
3
4
5
// ✅ Good: controlled input with onChange
<input
    value={something}
    onChange={(e) => setSomething(e.target.value)}
/>

Если значение намеренно предназначено только для чтения, добавьте параметр readOnly, чтобы подавить ошибку:

1
2
// ✅ Good: readonly controlled input without on change
<input value={something} readOnly={true} />

Мой флажок не обновляется, когда я нажимаю на него

Если вы отобразите флажок с checked, но без onChange, вы увидите ошибку в консоли:

1
2
// 🔴 Bug: controlled checkbox with no onChange handler
<input type="checkbox" checked={something} />

Ошибка

Вы предоставили свойство checked полю формы без обработчика onChange. Это приведет к тому, что поле будет доступно только для чтения. Если поле должно быть изменяемым, используйте defaultChecked. В противном случае установите либо onChange, либо readOnly.

Как следует из сообщения об ошибке, если вы хотите только указать начальное значение, передайте defaultChecked вместо этого:

1
2
// ✅ Good: uncontrolled checkbox with an initial value
<input type="checkbox" defaultChecked={something} />

Если вы хотите управлять этим флажком с помощью переменной состояния, укажите обработчик onChange:

1
2
3
4
5
6
// ✅ Good: controlled checkbox with onChange
<input
    type="checkbox"
    checked={something}
    onChange={(e) => setSomething(e.target.checked)}
/>

Для флажков нужно читать e.target.checked, а не e.target.value.

Если флажок намеренно предназначен только для чтения, добавьте параметр readOnly, чтобы подавить ошибку:

1
2
3
4
5
6
// ✅ Good: readonly controlled input without on change
<input
    type="checkbox"
    checked={something}
    readOnly={true}
/>

Мой каретка ввода перескакивает в начало при каждом нажатии клавиши

Если вы управляете вводом, вы должны обновить его переменную состояния до значения ввода из DOM во время onChange.

Вы не можете обновить ее до значения, отличного от e.target.value (или e.target.checked для флажков):

1
2
3
4
function handleChange(e) {
    // 🔴 Bug: updating an input to something other than e.target.value
    setFirstName(e.target.value.toUpperCase());
}

Вы также не можете обновлять его асинхронно:

1
2
3
4
5
6
function handleChange(e) {
    // 🔴 Bug: updating an input asynchronously
    setTimeout(() => {
        setFirstName(e.target.value);
    }, 100);
}

Чтобы исправить свой код, обновите его синхронно с e.target.value:

1
2
3
4
function handleChange(e) {
    // ✅ Updating a controlled input to e.target.value synchronously
    setFirstName(e.target.value);
}

Если это не устраняет проблему, возможно, что ввод удаляется и добавляется из DOM при каждом нажатии клавиши. Это может произойти, если вы случайно сбрасываете состояние при каждом повторном рендере, например, если input или один из его родителей всегда получает другой атрибут key, или если вы вложены определения функций компонентов (что не поддерживается и приводит к тому, что "внутренний" компонент всегда считается другим деревом).

Я получаю ошибку: "Компонент изменяет неконтролируемый вход на контролируемый"

Если вы передаете компоненту value, оно должно оставаться строкой в течение всего времени его работы.

Вы не можете сначала передать value={undefined}, а затем передать value="some string", потому что React не будет знать, хотите ли вы, чтобы компонент был неуправляемым или управляемым. Управляемый компонент всегда должен получать строковое value, а не null или undefined.

Если ваше value приходит из API или переменной состояния, оно может быть инициализировано в null или undefined. В этом случае либо изначально установите его в пустую строку (''), либо передайте value={someValue ?? ''}, чтобы убедиться, что value является строкой.

Аналогично, если вы передаете checked флажку, убедитесь, что это всегда булево значение.

Комментарии