Home > Enterprise >  Input not focusing the first time key is pressed
Input not focusing the first time key is pressed

Time:01-13

I want to make an input appear (go from display: none to display: block) when / is pressed. Then the input should be focused (the cursor should be in the input).

import React from "react";
import { useState, useEffect, useRef, ChangeEvent } from "react";
import { createRoot } from "react-dom/client";

function App() {
  const [inputValue] = useState("");
  const [isInputFocused, setIsInputFocused] = useState(false);
  const inputRef = useRef<HTMLInputElement | null>(null);

  function handleInputChange(event: ChangeEvent<HTMLInputElement>) {
    // some code
  }

  function handleInputBlur(event: ChangeEvent<HTMLInputElement>) {
    setIsInputFocused(false);
  }

  function handleDocumentKeyDown(event: any) {
    if (event.key === "/") {
      event.preventDefault();
      setIsInputFocused(true);
      inputRef.current?.focus();
    }
  }

  useEffect(() => {
    inputRef.current?.focus();

    document.addEventListener("keydown", handleDocumentKeyDown);

    return () => {
      document.removeEventListener("keydown", handleDocumentKeyDown);
    };
  }, []);

  return (
    <div id="keys" className={isInputFocused ? "active" : "inactive"}>
      <input
        ref={inputRef}
        type="text"
        onChange={handleInputChange}
        onBlur={handleInputBlur}
        value={inputValue}
      />
    </div>
  );
}

const root = createRoot(document.getElementById("root")!);
root.render(<App />);

This works as expected ... except the first time you press /, the input appears, but it's not focused (the cursor doesn't jump to the input). You have to press / again for the input to be focused.

Why is this? And how to change the code so that the input is focused the first time you press /?

Live example:

Edit React Input Focus

CodePudding user response:

As @ghybs said, setting state is async, so the actual re-render will happen at the next tick.

This means you can't immediately expect the input field to be rendered as soon as you set the isInputFocused state to true.

It needs to wait till the next tick.

Easiest way to wait till the next tick in React is using setTimeout:

setIsInputFocused(true);
setTimeout(() => {
  inputRef.current?.focus();
}, 0);

A more elegant solution is to wait for isInputFocused state change and apply the focus action:

useEffect(() => {
  if (isInputFocused) {
    inputRef.current?.focus();
  }
}, [isInputFocused]);
  • Related