I have encounter this problem, the .focus()
only works with setTimeout
if i take it out and it stop working. can anyone explain me what's the reason for that, possible i am doing it incorrectly and how can i fix the problem.
componentDidMount() {
React.findDOMNode(this.refs.titleInput).getElementsByTagName('input')[0].focus();
}
works example with setTimeout
componentDidMount() {
setTimeout(() => {
React.findDOMNode(this.refs.titleInput).getElementsByTagName('input')[0].focus();
}, 1);
}
JXS
<input ref="titleInput" type="text" />
and i have followed this example React set focus on input after render
render function
render() {
const {title, description, tagtext, siteName} = (this.state.selected !== undefined) ? this.state.selected : {};
const hasContentChangedYet = this.hasContentChangedYet(title, description);
return (
<div>
<h2 className={styles.formMainHeader}>Edit Meta-Data Form</h2>
<table className={styles.formBlock}>
<tbody>
<tr>
<td className={styles.tagEditLabel}>
Tag
</td>
<td className={styles.inputFieldDisableContainer}>
{tagtext}
</td>
</tr>
<tr>
<td className={styles.tagEditLabel}>
Site
</td>
<td className={styles.inputFieldDisableContainer}>
{siteName}
</td>
</tr>
<tr>
<td className={styles.tagEditLabel}>
Title
</td>
<td className={styles.inputFieldContainer}>
<ReactInputField
ref="titleInput"
id="title"
defaultValue={(title) ? title : ''}
onChange={this.onInputChange}
placeholder="Title"
clearTool={true} />
</td>
</tr>
<tr>
<td className={styles.tagEditLabel}>
Description
</td>
<td className={styles.inputFieldContainer}>
<ReactInputField
id="description"
defaultValue={(description) ? description : ''}
onChange={this.onInputChange}
placeholder="Description"
clearTool={true} />
</td>
</tr>
</tbody>
</table>
<div className={styles.formFooter}>
<button id="save-button" className={styles.saveButton} disabled={!hasContentChangedYet} onClick={() => this.handleSavePressed()}>
Save
</button>
<button id="form-cancel-button" className={styles.cancelButton} onClick={this.actions.form.cancelUpdateToTagData}>
Cancel
</button>
</div>
</div>
);
}
A React ref most commonly returns undefined or null when we try to access its current property before its corresponding DOM element is rendered. To get around this, access the ref in the useEffect hook or when an event is triggered.
It is a general rule of thumb to avoid using refs unless you absolutely have to. The official React documentation outlined only three possible use cases where refs are entirely considered useful for lack of better alternatives: Managing focus, text selection, or media playback. Triggering imperative animations.
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 itscomponentDidMount
method is called; this change should be observable only if your component calls a parent component's callback within yourcomponentDidMount
, 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 callingfindDOMNode()
inrender()
on a component that has yet to be created) an exception will be thrown.
And from the docs on the ref string attribute:
Assign a
ref
attribute to anything returned fromrender
such as:<input ref="myInput" />
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With