54

I'm trying to implement a very simple use case, a UI feature, where:

  1. There is a label with some content in it
  2. If clicked, a text input replaces it with the content of label available
  3. User can edit the content
  4. When enter is pressed, the input hides and label is back with updated content

I could get finally all correct (in fact with a MongoBD backend, redux, etc.), and the only thing I couldn't ever do (paying a complete day in googling and reading S.O.F similar posts) was this:

When my text input appears, I can't transfer focus to it! First I tired this way:

<div className={((this.state.toggleWordEdit) ? '' : 'hidden')}>
<input id={this.props.word._id} className="form-control"
        ref="updateTheWord" 
        defaultValue={this.state.word}
        onChange={this.handleChange}
        onKeyPress={this.handleSubmit}
        autoFocus={this.state.toggleWordEdit}/></div>
    <div className={((this.state.toggleWordEdit) ? 'hidden' : '')}>
      <h3 onClick={this.updateWord}>
        {this.state.word}</h3>
    </div>

but autoFocus sure didn't work (I "guess" because the form is rendered, but in hidden state, making autoFocus useless).

Next I tried in my this.updateWor, many of suggestions I found on google and S.O.F.:

this.refs.updateTheWord.focus();

which together with similar suggestions all didn't work. Also I tried to fool React just to see if at all I can do something! I used real DOM:

    const x = document.getElementById(this.props.word._id);
    x.focus();

and it didn't work either. One thing I even could not understand to put into word is a suggestion like this: having ref as a method (I "guess") I didn't even try it because I have multiples of these components and I need ref to further get value of, per component, and I couldn't imagine if my ref is not named, how I could get the value of!

So could you please give an idea, helping me to understand that in case I'm not using a Form (because I need a single input box replacing a label) how I could set its focus when it's CSS (Bootstrap) class is losing 'hidden' please?

Ariam1
  • 1,673
  • 2
  • 13
  • 32
  • 1
    does this work? c.focus()} /> – Brian Mar 31 '17 at 16:35
  • 1
    in this case, as soon as the input is rendered the callback defined by ref will be executed, passing in a reference to the current element. you can use this callback function to define a reference to the dom node - for example ref={c => this.refs.c = c}. then, you can use this.refs normally (just make sure it's defined beforehand). – Brian Mar 31 '17 at 19:34

7 Answers7

49

The way you have used refs is not the most preferred way or else its not the best practice anymore . try some thing like this

class MyClass extends React.Component {
  constructor(props) {
    super(props);
    this.focus = this.focus.bind(this);
  }

  focus() {
    this.textInput.current.focus();
  }

  render() {

    return (
      <div>
        <input
          type="text"
          ref={(input) => { this.textInput = input; }} />
        <input
          type="button"
          value="Set Focus"
          onClick={this.focus}
        />
      </div>
    );
  }
}

Update
From React 16.3 upwards you can use the React.createRef() API

class MyClass extends React.Component {
  constructor(props) {
    super(props);
    // create a ref to store the textInput DOM element
    this.textInput = React.createRef();
    this.focus = this.focus.bind(this);
  }

  focus() {
    // Explicitly focus the text input using the raw DOM API
    // Note: we're accessing "current" to get the DOM node
    this.textInput.current.focus();
  }

  render() {
    // tell React that we want to associate the <input> ref
    // with the `textInput` that we created in the constructor
    return (
      <div>
        <input
          type="text"
          ref={this.textInput} />
        <input
          type="button"
          value="Set Focus"
          onClick={this.focus}
        />
      </div>
    );
  }
}

From React 18.xx upwards you can use the useRef Hook

import React, { useRef } from "react";

export const Form = () => {
  const inputRef = useRef(null);

  const focus = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input type="text" ref={inputRef} />
      <input type="button" value="Set Focus" onClick={focus} />
    </div>
  );
};
TRomesh
  • 4,323
  • 8
  • 44
  • 74
  • 10
    How would I do that dynamically if I have a list of input fields and would like to focus on next input field when user hits enter? – Avi Kaminetzky Oct 03 '17 at 16:52
  • @AvremelKaminetzky Did you find a solution to your focus issues? If I understand your comment correctly I have a similar issue I'm struggling with. I have a Stateless React component `InputStepper` that should focus on the first input on load and then move focus after keyup to the next InputStepper input. However, I see the input focus and then lose focus. When I inspect the element `` is set but it still does not hold the focus. Is this an issue with React and form/input and focus? – Rockin4Life33 Jun 06 '18 at 15:49
  • @Anovative I can't recall the details, but here was my (hacky) solution: https://stackoverflow.com/a/46777890/4822174 – Avi Kaminetzky Jun 06 '18 at 16:20
  • If not working, especially for newer React, try `inputRef` instead of `ref`. I could programmatically focus `TextField`. – ghchoi Apr 30 '20 at 11:01
  • Does anyone have an implementation using function components? – Pedro Silveira Sep 13 '20 at 22:00
23

Just add autofocus attribute to the input. (of course in JSX it is autoFocus)

<input autoFocus ...
Mehdi Dehghani
  • 10,970
  • 6
  • 59
  • 64
9

useFocus hook

// General Focus Hook
const useFocus = (initialFocus = false, id = "") => {
    const [focus, setFocus] = useState(initialFocus)
    const setFocusWithTrueDefault = (param) => setFocus(isBoolean(param)? param : true)
    return ([
        setFocusWithTrueDefault, {
            autoFocus: focus,
            key: `${id}${focus}`,
            onFocus: () => setFocus(true),
            onBlur: () => setFocus(false),
        },
    ])
}


const FocusDemo = () => {

    const [labelStr, setLabelStr] = useState("Your initial Value")
    const [setFocus, focusProps] = useFocus(true)

    return (
        <> {/* React.Fragment */}
            <input
                onChange={(e)=> setLabelStr(e.target.value)}
                value={labelStr}
                {...focusProps}
            />
            <h3 onClick={setFocus}>{labelStr}</h3>
        </>
    )
    
}

For a more complete demo click here.

Ben Carp
  • 24,214
  • 9
  • 60
  • 72
  • 1
    There seem to be no link for complete demo. Is it available somewhere? – llamerr Oct 01 '19 at 11:40
  • 2
    @llamerr, Today I prefer a similar but slightly different approach: https://stackoverflow.com/questions/28889826/set-focus-on-input-after-render/54159564#54159564. I prefer it theoretically as it's shorter and easier to understand, but also practically, as it seems from my experience to work better on long lists. Please give it a shot and let me know what you think. If you agree I'll update my solution here as well. There might be a demo for this solution somewhere, or if you still find it necessary I can prepare one. – Ben Carp Oct 01 '19 at 14:04
  • This working. Thank you! – Ernesto Rojas Jan 12 '22 at 03:55
3

In addition to the previous answers, I've added setTimeout to make it work

handleClick() {


    if (this.searchInput) {
        setTimeout(() => {

            this.searchInput.focus();

        }, 100);
    }
}

where searchInput is the jsx ref of the input

<input
      type="text"
      name="searchText"
      ref={(input) => { this.searchInput = input; }}
      placeholder="Search" />

and the handleClick() is an onClick handler to any element

Ali Kleit
  • 3,069
  • 2
  • 23
  • 39
1

@BenCarp's answer in typescript

Pass the inputRef to an input and just call setFocus to set the focus to it.

export const useInputFocus = (): [MutableRefObject<HTMLInputElement | undefined>, () => void] => {
  const inputRef = useRef<HTMLInputElement>();
  const setFocus = (): void => {
    const currentEl = inputRef.current;
    if (currentEl) {
      currentEl.focus();
    }
  };
  return [inputRef, setFocus];
};
moto
  • 946
  • 10
  • 27
1

You can use "useRef" hook and make a reference to your input control, then use your reference.current.focus()

Zsolt Meszaros
  • 21,961
  • 19
  • 54
  • 57
H. Rashed
  • 11
  • 4
0

Use componentDidUpdate method to every time update the component

componentDidUpdate(prevProps, prevState) {
     this.input.focus();
}
Faraz Ahmed
  • 1,467
  • 2
  • 18
  • 33