【问题标题】:react ref with focus() doesn't work without setTimeout (my example)没有 setTimeout(我的例子),用 focus() 反应 ref 不起作用
【发布时间】:2016-06-02 00:35:30
【问题描述】:

我遇到过这个问题,.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>
        );
    }

【问题讨论】:

    标签: reactjs


    【解决方案1】:

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

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

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

    componentDidMount 的使用确保代码仅在 调用它的组件安装时运行(请参阅下面的文档引用)。

    注意调用 React.findDOMNode is discouraged:

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

    注意

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

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

    来自ref string attribute 上的文档:

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

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

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

    或者,您可以消除对代码的需求,并使用 JSX autoFocus 属性:

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

    【讨论】:

      【解决方案2】:

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

      在父组件中,我渲染了一个带有 InputText 的 Primereact 对话框:

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

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

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

      只有在渲染完成并调用 setState 回调函数后,输入元素才会获得焦点。

      不使用 setState 回调,在某些情况下您可以简单地使用:

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-03-31
        • 2019-03-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-21
        • 2019-07-01
        相关资源
        最近更新 更多