DEV Community

jennypollard
jennypollard

Posted on • Updated on

React: сфокусировать поле ввода по чекбоксу

Как установить фокус на поле ввода (инпут) при изменении чекбокса?

Даны чекбокс <input type="checkbox"> и поле ввода <input type="text">. Хочу фокусировать поле ввода, когда пользователь отмечает чекбокс — так пользователю будет понятно, что от него ожидается ввод текста, если чекбокс включён.
Напишу компонент, который показывает чекбокс и поле ввода, значение для поля ввода сохраню с помощью хуков useState и useCallback:

export const App = () => {
  const [value, setValue] = useState("");
  const handleInputChange = useCallback(
    (event) => setValue(event.target.value),
    []
  );

  return (
    <main>
      <input type="checkbox" />
      <input type="text" value={value} onChange={handleInputChange} />
    </main>
  );
};
Enter fullscreen mode Exit fullscreen mode

Добавлю обработчик на изменение чекбокса: покажу алерт с новым значением. Обработчик события вызывается с объектом event, в свойстве target лежит элемент, который вызвал событие, в моем случае event.target — чекбокс. Новое значение чекбокса можно получить из его свойства checked: event.target.checked:

export const App = () => {
  const [value, setValue] = useState("");
  const handleInputChange = useCallback(
    (event) => setValue(event.target.value),
    []
  );
  const handleChange = useCallback((event) => {
    alert(event.target.checked);
  }, []);

  return (
    <div className="App">
      <input type="checkbox" onChange={handleChange} />
      <input type="text" value={value} onChange={handleInputChange} />
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Теперь, при изменении чекбокса, установлю фокус в поле ввода. У элементов, которые могут получать фокус ввода, есть метод focus. Чтобы вызвать этот метод у поля ввода, его нужно найти - в этом поможет механизм ref-ов и хук useRef.
Создам ref и передам его пропсой полю ввода:

const inputRef = useRef();
// ...
<input 
  type="text"
  value={value}
  onChange={handleInputChange}
  ref={inputRef}
/>;

Enter fullscreen mode Exit fullscreen mode

React, после рендера элементов, поместит в inputRef ссылку на поле ввода. У всех ref-ов есть свойство current, в котором хранится значение ref-а. Воспользуюсь им, чтобы вызвать метод focus у элемента, для этого в обработчик изменения чекбокса добавлю inputRef.current.focus():

export const App = () => {
  const [value, setValue] = useState("");
  const inputRef = useRef();
  const handleInputChange = useCallback((e) => setValue(e.target.value), []);
  const handleChange = useCallback(
    (e) => {
      if (e.target.checked) {
        inputRef.current.focus();
      }
    },
    [inputRef]
  );
  return (
    <div className="App">
      <input type="checkbox" onChange={handleChange} />
      <input
        type="text"
        value={value}
        onChange={handleInputChange}
        ref={inputRef}
      />
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Иногда может оказаться, что решение с использованием ref-ов слишком сложное. Тогда можно попробовать найти нужный элемент с помощью DOM API — например, document.querySelector. Чтобы найти элемент используя querySelector, его нужно как-то идентифицировать, добавлю ему id. Затем, в обработчике изменения чекбокса, заменю ref на вызов document.querySelector:

export const App = () => {
  const [value, setValue] = useState("");
  const handleInputChange = useCallback((e) => setValue(e.target.value), []);
  const handleChange = useCallback((e) => {
    if (e.target.checked) {
      document.querySelector("#input").focus();
    }
  }, []);
  return (
    <div className="App">
      <input type="checkbox" onChange={handleChange} />
      <input
        id="input"
        type="text"
        value={value}
        onChange={handleInputChange}
      />
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Использовать documenent.querySelector нежелательно:

  • нет гарантии, что найдет нужный элемент — сколько еще может быть на странице элементов с таким же селектором?
  • компонент перестанет работать в окружении, где нет document.
  • querySelector будет искать элемент по всему дереву, а React сохранит ссылку на элемент в ref-е при рендере, что быстрее.

Поэтому вариант с ref-ами более стабильный и предсказуемый.

Top comments (0)