使用focus()做出反应没有setTimeout(我的例子)没有工作

时间:2016-02-20 10:55:29

标签: reactjs

我遇到了这个问题,.focus()只适用于setTimeout,如果我把它拿出来它就会停止工作。任何人都可以解释我的原因是什么,可能我做错了,我该如何解决这个问题。

    componentDidMount() {
        React.findDOMNode(this.refs.titleInput).getElementsByTagName('input')[0].focus();
    }

使用setTimeout

作为示例
componentDidMount() {
    setTimeout(() => {
        React.findDOMNode(this.refs.titleInput).getElementsByTagName('input')[0].focus();
    }, 1);
}

JXS

<input ref="titleInput" type="text" />

我已按照此示例React set focus on input after render

渲染功能

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>
        );
    }

2 个答案:

答案 0 :(得分:7)

在看到问题的更新后,我意识到你已经将深度嵌套的HTML传递给了 render 函数,并且你感兴趣的输入元素确实无法在< em> componentDidMount 调用祖先元素。如React v0.13 Change Log中所述:

  

ref解析顺序略有变化,以便在调用componentDidMount方法后立即获得对组件的引用;只有当您的组件在componentDidMount内调用父组件的回调时,才能观察到此更改,这是一种反模式,无论如何都应该避免

这是你的情况。因此,您必须将HTML结构分解为单独呈现的元素,如here所述,然后您将在其自己的 componentDidMount 回调中访问input元素;或者你只是坚持使用计时器黑客。

componentDidMount 的使用确保代码仅在挂载>的组件 时运行(请参阅文档中的文章) )。

请注意,调用 React.findDOMNode is discouraged

  

在大多数情况下,您可以将引用附加到DOM节点,并完全避免使用findDOMNode

     
    

注意

         

findDOMNode()是用于访问底层DOM节点的转义符号。在大多数情况下,不鼓励使用此逃生舱,因为它会刺穿组件抽象。

         

findDOMNode()仅适用于已安装的组件(即已放置在DOM中的组件)。如果您尝试在尚未挂载的组件上调用此方法(如在findDOMNode()中调用尚未创建的组件上的render()),则会抛出异常。

  

来自ref string attribute上的文档:

  
      
  1. 为从ref返回的任何内容分配render属性,例如:

     <input ref="myInput" />
    
  2.   
  3. 在其他一些代码(通常是事件处理程序代码)中,通过this.refs访问支持实例,如下所示:

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

或者,您可以省去代码,并使用JSX autoFocus属性:

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

答案 1 :(得分:1)

  

使用setTimeout()是个坏主意,而使用componentDidMount()则无关紧要。您可以在以下示例中找到问题的答案:

在父组件中,我呈现一个带有InputText的引言对话框:

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

最初, this.state.visible 为false,并且对话框是隐藏的。 为了显示对话框,我通过调用showDlg()重新渲染父组件,其中nameInp是对InputText的引用:

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

仅在完成渲染并调用setState回调函数之后,input元素才会获得焦点。

在某些情况下,您可以使用以下方法代替使用setState回调:

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

但是,每次(重新)渲染组件时(包括在InputText隐藏的情况下)都会调用componentDidUpdate()。

另请参阅:https://reactjs.org/docs/react-component.html#setstate