react ref with focus() doesn't work without setTimeout (my example)

After seeing the update to the question, I realise that you have deeply nested HTML passed to the render function, and the input element of your interest will indeed not be available at the time of the componentDidMount call on the ancestor element. As stated in the React v0.13 Change Log:

ref resolution order has changed slightly such that a ref to a component is available immediately after its componentDidMount method is called; this change should be observable only if your component calls a parent component's callback within your componentDidMount, which is an anti-pattern and should be avoided regardless

This is your case. So either you have to break down the HTML structure into separately rendered elements, as described here, and then you would access the input element in its own componentDidMount callback; or you just stick with the timer hack you have.

Use of componentDidMount makes sure the code runs only when the component on which it is called is mounted (see quote from docs further down).

Note that calling React.findDOMNode is discouraged:

In most cases, you can attach a ref to the DOM node and avoid using findDOMNode at all.

Note

findDOMNode() is an escape hatch used to access the underlying DOM node. In most cases, use of this escape hatch is discouraged because it pierces the component abstraction.

findDOMNode() only works on mounted components (that is, components that have been placed in the DOM). If you try to call this on a component that has not been mounted yet (like calling findDOMNode() in render() on a component that has yet to be created) an exception will be thrown.

And from the docs on the ref string attribute:

  1. Assign a ref attribute to anything returned from render such as:

     <input ref="myInput" />
    
  2. In some other code (typically event handler code), access the backing instance via this.refs as in:

    var input = this.refs.myInput;  
    var inputValue = input.value;  
    var inputRect = input.getBoundingClientRect();  
    

Alternatively, you could eliminate the need of the code, and use the JSX autoFocus attribute:

<ReactInputField
        ref="titleInput"
        autoFocus
        ... />

Using setTimeout() is a bad idea and using componentDidMount() is irrelevant. You may find the answer to your question in the following example:

In a parent component I render a primereact Dialog with an InputText in it:

<Dialog visible={this.state.visible} ...>
  <InputText ref={(nameInp) => {this.nameInp = nameInp}} .../>
  ...
</Dialog>

Initially, this.state.visible is false and the Dialog is hidden. To show the Dialog, I re-render the parent component by calling showDlg(), where nameInp is the ref to InputText:

showDlg() {
  this.setState({visible:true}, ()=>{
    this.nameInp.element.focus();
  });
}

The input element gets the focus only after rendering has been accomplished and the setState callback function called.

Instead of using the setState callback, in some cases you may simply use:

componentDidUpdate(){
  this.nameInp.element.focus();
}

However, componentDidUpdate() is being called every time you (re)render the component, including in case the InputText is hidden.

See also: https://reactjs.org/docs/react-component.html#setstate

Tags:

Reactjs