react ref with focus() 在没有 setTimeout 的情况下不起作用(我的例子)

IT技术 reactjs
2021-04-14 04:49:49

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

剑道

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

我已经按照这个示例React 在渲染后将焦点设置在输入上

渲染功能

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个回答

看到问题的更新后,我意识到您将深层嵌套的HTML传递给了render函数,并且您感兴趣的输入元素在祖先元素上调用componentDidMount时确实不可用React v0.13 更改日志中所述

ref解析顺序略有变化,以便在componentDidMount调用方法后立即可以使用对组件的引用这种变化应该是可见的,仅仅如果组件调用你的内父组件的回调componentDidMount,这是一种反模式,应该不分避免

这是你的情况。因此,无论你的HTML结构分解成单独渲染的元素,如所描述这里,然后你就会访问自己的输入元素componentDidMount回调; 或者你只是坚持你拥有的计时器黑客。

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

请注意,不鼓励调用React.findDOMNode

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

笔记

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

findDOMNode()仅适用于已安装的组件(即已放置在 DOM 中的组件)。如果你试图调用这个尚未安装,但(像调用组件findDOMNode()render(),目前尚未将要创建的组件)的异常将被抛出。

来自ref string 属性的文档

  1. ref属性分配给render以下返回的任何内容

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

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

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

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

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

在父组件中,我渲染了一个带有 InputText 的 primereact Dialog:

<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