Alita
Alita

Reputation: 899

How do you focus and select an input when it loads?

I was unable to find good answer to the question of focusing and selecting an input element on load; initially I just used a useRef(), set it as the ref and then on component load (useEffect(,[])) I would ref.current.focus() / ref.current.select(). But input's built in autoFocus and onFocus are much simpler solutions.

In other words, consider the following code snippet where we want to focus on the input when you press the button (it doesn't work here)

// Get hook functions (specific for use with stackoverflow code snippet)
const { useState } = React;

const Example = ({title}) => {
  // hook that keeps track of the editing state
  const [editing, setEditing] = useState(false);
  
  // switch to input when button is pressed
  const InputComponent = () => {
    if (!editing) { 
     return ( <span>click for input</span>)
    }
    else {
      return ( <input /> )
    }
  }

  return (
    <div>
      <p>{title}</p>
      <p>{InputComponent()}</p>
      <button onClick={() => setEditing(!editing)}>
        Click me
      </button>
    </div>
  );
};

// Render it
ReactDOM.render(
  <Example title="The goal is to have the input automatically focused" />,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

Now, if we use references

const { useState, useRef, useEffect } = React;

const Example = ({title}) => {
  const [editing, setEditing] = useState(false);
  // useRef for a reference to the input
  const inputRef = useRef();

  const InputComponent = () => {
    if (!editing) { 
     return ( <span>click for input</span>)
    }
    else {
      // set the ref as a reference to the input
      return ( <input ref={inputRef}/> )
    }
  }
  
  // when editing updates, run this code
  useEffect(() => {
    // when editing is true, focus the input
    if (editing) {inputRef.current.focus()}
  }, [editing])

  return (
    <div>
      <p>{title}</p>
      <p>{InputComponent()}</p>
      <button onClick={() => setEditing(!editing)}>
        Click me
      </button>
    </div>
  );
};

// Render it
ReactDOM.render(
  <Example title="The goal is to have the input automatically focused" />,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

further if you want to select the value of the input when it's focused you can do the following:

const { useState, useRef, useEffect } = React;

const Example = ({title}) => {
  const [editing, setEditing] = useState(false);
  // define a state variable for the input value
  const [inputValue, setValue] = useState("value of the input");
  const inputRef = useRef();

  const InputComponent = () => {
    if (!editing) { 
     return ( <span>click for input</span>)
    }
    else {
      
      return ( 
        <input 
        ref={inputRef} 
        // define the value of the input
        value={inputValue}
        // when the input is changed, update the state variable
        onChange={(event) => setValue(event.target.value)}
        /> 
      )
    }
  }
  
  useEffect(() => {
    if (editing) {
      inputRef.current.focus();
      // focus and select the value of the input
      inputRef.current.select();
    }
  }, [editing])

  return (
    <div>
      <p>{title}</p>
      <p>{InputComponent()}</p>
      <button onClick={() => setEditing(!editing)}>
        Click me
      </button>
    </div>
  );
};

// Render it
ReactDOM.render(
  <Example title="The goal is to have the input automatically focused" />,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

This methodology would allow you to set some base value of the input (like a stylized way of representing the value, and then when the user clicks that stylize value it begins editing).

This solution is well and good, but it's bulky and requires that you pass references down to the component in some cases, and is just generally not clean. So is there a better solution? yes....

Upvotes: 1

Views: 2115

Answers (1)

Alita
Alita

Reputation: 899

Here's a simplified version that will accomplish the same effect but much simpler

const { useState, useRef, useEffect } = React;

const Example = ({title}) => {
  const [editing, setEditing] = useState(false);
  const [inputValue, setValue] = useState("value of the input");
  // no need for a reference

  const InputComponent = () => {
    if (!editing) { 
     return ( <span>click for input</span>)
    }
    else {
      return ( 
        <input 
          // don't need to set an input reference
          onChange={(event) => setValue(event.target.value)}
          value={inputValue}
          onFocus={(event) => event.target.select()} // selects the value on load
          autoFocus // focuses on load
        /> 
      )
    }
  }
  
  // no need for a useEffect
  return (
    <div>
      <p>{title}</p>
      <p>{InputComponent()}</p>
      <button onClick={() => setEditing(!editing)}>
        Click me
      </button>
    </div>
  );
};

// Render it
ReactDOM.render(
  <Example title="The goal is to have the input automatically focused" />,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

There you go, a much faster, simpler, and easier to understand implementation of focusing on an input on load. Cheers! :)

Upvotes: 2

Related Questions