【问题标题】:How to set focus on an input field after rendering?渲染后如何在输入字段上设置焦点?
【发布时间】:2020-11-05 09:59:52
【问题描述】:

在渲染组件后设置焦点在特定文本字段上的反应方式是什么?

文档似乎建议使用 refs,例如:

在渲染函数中我的输入字段设置ref="nameInput",然后调用:

this.refs.nameInput.getInputDOMNode().focus(); 

但是我应该在哪里称呼它呢?我已经尝试了几个地方,但我无法让它工作。

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    专注于安装

    如果您只想在元素安装(初始渲染)时对其进行聚焦,只需简单地使用 autoFocus 属性即可。

    <input type="text" autoFocus />
    

    动态焦点

    要动态控制焦点,请使用通用函数来隐藏组件的实现细节。

    React 16.8 + 功能组件 - useFocus hook

    const FocusDemo = () => {
    
        const [inputRef, setInputFocus] = useFocus()
    
        return (
            <> 
                <button onClick={setInputFocus} >
                   Focus
                </button>
                <input ref={inputRef} />
            </>
        )
        
    }
    
    
    const useFocus = () => {
        const htmlElRef = useRef(null)
        const setFocus = () => {htmlElRef.current &&  htmlElRef.current.focus()}
    
        return [ htmlElRef, setFocus ] 
    }
    

    Full Demo

    React 16.3 + 类组件 - 利用焦点

    class App extends Component {
      constructor(props){
        super(props)
        this.inputFocus = utilizeFocus()
      }
    
      render(){
        return (
          <> 
              <button onClick={this.inputFocus.setFocus}>
                 Focus
              </button>
              <input ref={this.inputFocus.ref}/>
          </>
        )
      } 
    }
    
    const utilizeFocus = () => {
        const ref = React.createRef()
        const setFocus = () => {ref.current &&  ref.current.focus()}
    
        return {setFocus, ref} 
    }
    

    Full Demo

    【讨论】:

    • 这个答案包含 React Hooks 的正确方法。极好的!它不会在 TypeScript 中按原样进行类型检查,而是一种(丑陋的)使其工作的方法:(1)(htmlElRef.current as any).focus() 和(2)return {htmlElRef, setFocus} 而不是数组。
    • @AhmedFasih,我知道你在说什么,但我认为它超出了这个线程的范围。如果您返回一个对象,则更难以控制变量的名称,如果您想将useFocus 用于多个元素,这可能会成为问题。
    • 这是useFocus 用打字稿写的。 gist.github.com/carpben/de968e377cbac0ffbdefe1ab56237573
    • @BenCarp 对钩子的小建议,最好将set 放在第二个位置,如const [inputRef, setInputFocus] = useFocus()。这更匹配 useState。首先是对象,然后是该对象的设置器
    • @Rubanov,谢谢。我根据您的建议调整了代码。
    【解决方案2】:

    打字稿中的 Ben Carp 解决方案

    React 16.8 + 函数式组件 - useFocus hook

    export const useFocus = (): [React.MutableRefObject<HTMLInputElement>, VoidFunction] => {
      const htmlElRef = React.useRef<HTMLInputElement>(null);
      const setFocus = React.useCallback(() => {
        if (htmlElRef.current) htmlElRef.current.focus();
      }, [htmlElRef]);
    
      return React.useMemo(() => [htmlElRef, setFocus], [htmlElRef, setFocus]);
    };
    

    【讨论】:

      【解决方案3】:

      没有自动对焦的简单解决方案:

      <input ref={ref => ref && ref.focus()}
          onFocus={(e)=>e.currentTarget.setSelectionRange(e.currentTarget.value.length, e.currentTarget.value.length)}
          />
      

      ref 触发焦点,并触发onFocus 计算结束并相应地设置光标。

      【讨论】:

        【解决方案4】:

        React 16.3 添加了一种新的便捷方式来处理这个问题,方法是在组件的构造函数中创建一个 ref 并像下面这样使用它:

        class MyForm extends Component {
          constructor(props) {
              super(props);
        
              this.textInput = React.createRef();
          }
        
          componentDidMount() {
            this.textInput.current.focus();
          }
        
          render() {
            return(
              <div>
                <input ref={this.textInput} />
              </div>
            );
          }
        }
        

        有关React.createRef的更多详细信息,您可以在React博客中查看this article

        更新:

        从React 16.8开始,useRef钩子可以用在函数组件中,达到同样的效果:

        import React, { useEffect, useRef } from 'react';
        
        const MyForm = () => {
          const textInput = useRef(null);
        
          useEffect(() => {
            textInput.current.focus();
          }, []);
        
          return (
            <div>
              <input ref={textInput} />
            </div>
          );
        };
        

        【讨论】:

        • 一个小改动:textInput.current?.focus();
        【解决方案5】:

        使用带有 Typescript 的 React Hooks / 功能组件,您可以使用 useRef hookHTMLInputElement 作为 useRef 的通用参数:

        import React, { useEffect, useRef } from 'react';
        
        export default function MyComponent(): JSX.Element {
            const inputReference = useRef<HTMLInputElement>(null);
        
            useEffect(() => {
                inputReference.current?.focus();
            }, []);
        
            return (
                <div>
                    <input ref={inputReference} />
                </div>
            );
        }
        

        或者如果使用reactstrap,请将inputReference 提供给innerRef 而不是ref

        import React, { useEffect, useRef } from 'react';
        import { Input } from 'reactstrap';
        
        export default function MyComponent(): JSX.Element {
            const inputReference = useRef<HTMLInputElement>(null);
        
            useEffect(() => {
                inputReference.current?.focus();
            }, []);
        
            return (
                <div>
                    <Input innerRef={inputReference} />
                </div>
            );
        }
        

        【讨论】:

        • 非常感谢!
        【解决方案6】:
        <input type="text" autoFocus />
        

        总是先尝试简单和基本的解决方案,对我有用。

        【讨论】:

        • 自动对焦元素可能会导致有视力和无视力用户的可用性问题,等等。我不推荐使用这个属性。
        【解决方案7】:

        根据更新后的语法,可以使用this.myRref.current.focus()

        【讨论】:

          【解决方案8】:

          由于这个错误有很多原因,我想我也会发布我面临的问题。对我来说,问题是我将输入呈现为另一个组件的内容。

          export default ({ Content }) => {
            return (
            <div className="container-fluid main_container">
              <div className="row">
                <div className="col-sm-12 h-100">
                  <Content />                                 // I rendered my inputs here
                </div>
              </div>
            </div>
            );
          }
          

          这是我调用上述组件的方式:

          <Component Content={() => {
            return (
              <input type="text"/>
            );
          }} />
          

          【讨论】:

            【解决方案9】:

            要将焦点移至新创建的元素,您可以将元素的 ID 存储在状态中并使用它来设置autoFocus。例如

            export default class DefaultRolesPage extends React.Component {
            
                addRole = ev => {
                    ev.preventDefault();
                    const roleKey = this.roleKey++;
                    this::updateState({
                        focus: {$set: roleKey},
                        formData: {
                            roles: {
                                $push: [{
                                    id: null,
                                    name: '',
                                    permissions: new Set(),
                                    key: roleKey,
                                }]
                            }
                        }
                    })
                }
            
                render() {
                    const {formData} = this.state;
            
                    return (
                        <GridForm onSubmit={this.submit}>
                            {formData.roles.map((role, idx) => (
                                <GridSection key={role.key}>
                                    <GridRow>
                                        <GridCol>
                                            <label>Role</label>
                                            <TextBox value={role.name} onChange={this.roleName(idx)} autoFocus={role.key === this.state.focus}/>
                                        </GridCol>
                                    </GridRow>
                                </GridSection>
                            ))}
                        </GridForm>
                    )
                }
            }
            

            这样,所有文本框都不会在页面加载时获得焦点(就像我想要的那样),但是当您按下“添加”按钮创建新记录时,新记录就会获得焦点。

            由于autoFocus 不会再次“运行”,除非重新挂载该组件,因此我不必费心取消设置this.state.focus(即当我更新其他状态时它不会继续抢回焦点)。

            【讨论】:

              【解决方案10】:

              在尝试了上面的很多选项但没有成功后,我发现这是因为我是 disabling 然后 enabling 导致焦点丢失的输入。

              我有一个道具sendingAnswer,它会在我轮询后端时禁用输入。

              <Input
                autoFocus={question}
                placeholder={
                  gettingQuestion ? 'Loading...' : 'Type your answer here...'
                }
                value={answer}
                onChange={event => dispatch(updateAnswer(event.target.value))}
                type="text"
                autocomplete="off"
                name="answer"
                // disabled={sendingAnswer} <-- Causing focus to be lost.
              />
              

              一旦我删除了禁用的道具,一切又开始工作了。

              【讨论】:

                【解决方案11】:

                @Dhiraj 的回答是正确的,为方便起见,您可以使用 autoFocus 道具在安装时自动聚焦输入:

                <input autoFocus name=...
                

                请注意,在 jsx 中它是 autoFocus(大写 F),这与不区分大小写的普通旧 html 不同。

                【讨论】:

                • 请注意,在 jsx 中,它的 autoFocus(大写 F)与不区分大小写的普通旧 html 不同。
                • 非常好,经过漫长的无果搜索后来到这里 :) 仅供参考 - 我最终使用了 React.DOM.input({ type: 'text', defaultValue: content, autoFocus: true, onFocus: function (e) {e.target.select();} })
                • 我发现自动对焦只适用于第一页渲染。请参阅codepen.io/ericandrewlewis/pen/PbgwqJ?editors=1111 输入应在 3 秒后聚焦。
                • 这个方法+1。值得一提的是,这不仅仅使用了 HTML5 不可靠的autofocus 属性,它实际上是uses focus() on DOM mount in react-dom,所以它是相当可靠的。
                • 不仅“为了方便”,而且如果您的组件是功能组件。
                【解决方案12】:

                你不需要getInputDOMNode??在这种情况下...

                当组件被挂载时,只需简单地获取reffocus() -- componentDidMount...

                import React from 'react';
                import { render } from 'react-dom';
                
                class myApp extends React.Component {
                
                  componentDidMount() {
                    this.nameInput.focus();
                  }
                
                  render() {
                    return(
                      <div>
                        <input ref={input => { this.nameInput = input; }} />
                      </div>
                    );
                  }
                
                }
                
                ReactDOM.render(<myApp />, document.getElementById('root'));
                

                【讨论】:

                  【解决方案13】:

                  这是正确的方法,如何自动对焦。当您使用回调而不是字符串作为参考值时,它会被自动调用。使用getDOMNode

                  render: function() {
                    return <TextInput ref={(c) => this._input = c} />;
                  },
                  componentDidMount: function() {
                    this._input.focus();
                  },
                  

                  【讨论】:

                  • 受控表单怎么样?
                  • @pixel67 还有。您可以在元素上设置参考,也可以在组件上设置参考。但是在使用它时您必须意识到这一点。所以你不会尝试访问输入的 .value,如果你在 React.Component 上设置了引用,它会包装 html 输入。
                  【解决方案14】:

                  自动对焦最适合我。我需要在双击时将一些文本更改为带有该文本的输入,所以这就是我最终得到的结果:

                  <input autoFocus onFocus={this.setCaretToEnd} value={this.state.editTodo.value} onDoubleClick={this.updateTodoItem} />
                  

                  注意:要解决 React 将插入符号放在文本开头的问题,请使用以下方法:

                  setCaretToEnd(event) {
                      var originalText = event.target.value;
                      event.target.value = '';
                      event.target.value = originalText;
                  }
                  

                  在这里找到: https://coderwall.com/p/0iz_zq/how-to-put-focus-at-the-end-of-an-input-with-react-js

                  【讨论】:

                    【解决方案15】:

                    我也有同样的问题,但我也有一些动画,所以我的同事建议使用 window.requestAnimationFrame

                    这是我的元素的 ref 属性:

                    ref={(input) => {input && window.requestAnimationFrame(()=>{input.focus()})}}
                    

                    【讨论】:

                      【解决方案16】:

                      阅读了几乎所有的答案,但没有看到getRenderedComponent().props.input

                      设置你的文本输入参考

                      this.refs.username.getRenderedComponent().props.input.onChange('');

                      【讨论】:

                      • 请在他们的代码上下文中进一步阐明您的答案。
                      【解决方案17】:

                      请注意,这些答案都不适用于material-ui TextField component。根据How to set focus to a materialUI TextField?,我不得不跳过一些圈子才能让它工作:

                      const focusUsernameInputField = input => {
                        if (input) {
                          setTimeout(() => {input.focus()}, 100);
                        }
                      };
                      
                      return (
                        <TextField
                          hintText="Username"
                          floatingLabelText="Username"
                          ref={focusUsernameInputField}
                        />
                      );
                      

                      【讨论】:

                      • 看起来如果你的组件正在动画中,对focus()的调用必须延迟到动画结束。
                      • 为我工作,但我使用了setTimeout(() =&gt; {}, 0);只是为了让代码更干净
                      【解决方案18】:

                      您应该改为在componentDidMountrefs callback 中执行此操作。像这样的

                      componentDidMount(){
                         this.nameInput.focus(); 
                      }
                      

                      class App extends React.Component{
                        componentDidMount(){
                          this.nameInput.focus();
                        }
                        render() {
                          return(
                            <div>
                              <input 
                                defaultValue="Won't focus" 
                              />
                              <input 
                                ref={(input) => { this.nameInput = input; }} 
                                defaultValue="will focus"
                              />
                            </div>
                          );
                        }
                      }
                          
                      ReactDOM.render(<App />, document.getElementById('app'));
                      <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.js"></script>
                      <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-dom.js"></script>
                      <div id="app"></div>

                      【讨论】:

                      • 这是正确的答案,但它对我不起作用,因为我的组件首先什么都不渲染,直到单击另一个按钮。这意味着它已经挂载了,所以我必须添加 this.refs.nameInput.getDOMNode().focus();在 componentDidUpdate 而不是 componentDidMount.
                      • 为什么,当 element.focus() 被调用时,它会将光标放在输入的开头?我在我的应用程序中看到了这个(我认为是一个)错误,在 chrome 中,实际上是在
                      • 警告:React.findDOMNode 已弃用。请改用 require('react-dom') 中的 ReactDOM.findDOMNode。
                      • @HuwDavies 我猜你会在&lt;input&gt; 元素上使用ref Callback Attribute。类似&lt;input ref={ (component) =&gt; ReactDOM.findDOMNode(component).focus() } /&gt;
                      • 为什么我们不直接使用 ref={(input) => { input.focus()}} ?这个解决方案对我来说很好。
                      【解决方案19】:

                      最简单的答案是在输入文本元素中添加 ref="some name" 并调用以下函数。

                      componentDidMount(){
                         this.refs.field_name.focus();
                      }
                      // here field_name is ref name.
                      
                      <input type="text" ref="field_name" />
                      

                      【讨论】:

                        【解决方案20】:

                        更新版本可以查看here

                        componentDidMount() {
                        
                            // Focus to the input as html5 autofocus
                            this.inputRef.focus();
                        
                        }
                        render() {
                            return <input type="text" ref={(input) => { this.inputRef = input }} />
                        })
                        

                        【讨论】:

                          【解决方案21】:

                          从 React 0.15开始,最简洁的方法是:

                          <input ref={input => input && input.focus()}/>
                          

                          【讨论】:

                          • 这也处理初始渲染之外的场景,而仅使用自动对焦则不能。
                          • 问题,什么时候输入是假的?我指的是箭头函数内部的表达式。
                          • @JaeGeeTee 在安装组件和/或卸载组件之前它为空(我不确定是哪种情况)。
                          • 唯一的问题是它将输入集中在任何可能不需要的重新渲染上..
                          • 在我的情况下不起作用(使用 Ant Design 输入组件)
                          【解决方案22】:

                          我刚刚遇到了这个问题,我正在使用 react 15.0.1 15.0.2 并且我正在使用 ES6 语法,并且从 v 之后的其他答案中并没有完全得到我需要的东西.15 数周前下降,部分 this.refs properties were deprecatedremoved

                          总的来说,我需要的是:

                          1. 在组件挂载时聚焦第一个输入(字段)元素
                          2. 关注第一个出错的输入(字段)元素(提交后)

                          我正在使用:

                          • React 容器/演示组件
                          • Redux
                          • 反应路由器

                          聚焦第一个输入元素

                          我在页面上的第一个&lt;input /&gt; 上使用了autoFocus={true},这样当组件挂载时,它就会获得焦点。

                          关注第一个出错的输入元素

                          这花费了更长的时间并且更加复杂。为了简洁起见,我保留了与解决方案无关的代码。

                          Redux 存储/状态

                          我需要一个全局状态来知道我是否应该设置焦点并在设置焦点时禁用它,所以当组件重新渲染时我不会继续重新设置焦点(我将使用componentDidUpdate()检查设置焦点。)

                          这可以按照您认为适合您的应用程序进行设计。

                          {
                              form: {
                                  resetFocus: false,
                              }
                          }
                          

                          容器组件

                          该组件需要设置resetfocus 属性和一个回调来清除该属性,如果它最终将焦点设置在自身上。

                          另外请注意,我将我的动作创建者组织成单独的文件,主要是因为我的项目相当大,我想将它们分成更易于管理的块。

                          import { connect } from 'react-redux';
                          import MyField from '../presentation/MyField';
                          import ActionCreator from '../actions/action-creators';
                          
                          function mapStateToProps(state) {
                              return {
                                  resetFocus: state.form.resetFocus
                              }
                          }
                          
                          function mapDispatchToProps(dispatch) {
                              return {
                                  clearResetFocus() {
                                      dispatch(ActionCreator.clearResetFocus());
                                  }
                              }
                          }
                          
                          export default connect(mapStateToProps, mapDispatchToProps)(MyField);
                          

                          演示组件

                          import React, { PropTypes } form 'react';
                          
                          export default class MyField extends React.Component {
                              // don't forget to .bind(this)
                              constructor(props) {
                                  super(props);
                                  this._handleRef = this._handleRef.bind(this);
                              }
                          
                              // This is not called on the initial render so
                              // this._input will be set before this get called
                              componentDidUpdate() {
                                  if(!this.props.resetFocus) {
                                      return false;
                                  }
                          
                                  if(this.shouldfocus()) {
                                      this._input.focus();
                                      this.props.clearResetFocus();
                                  }
                              }
                          
                              // When the component mounts, it will save a 
                              // reference to itself as _input, which we'll
                              // be able to call in subsequent componentDidUpdate()
                              // calls if we need to set focus.
                              _handleRef(c) {
                                  this._input = c;
                              }
                          
                              // Whatever logic you need to determine if this
                              // component should get focus
                              shouldFocus() {
                                  // ...
                              }
                          
                              // pass the _handleRef callback so we can access 
                              // a reference of this element in other component methods
                              render() {
                                  return (
                                      <input ref={this._handleRef} type="text" />
                                  );
                              }
                          }
                          
                          Myfield.propTypes = {
                              clearResetFocus: PropTypes.func,
                              resetFocus: PropTypes.bool
                          }
                          

                          概述

                          一般的想法是,每个可能有错误并被聚焦的表单字段都需要检查自身以及是否需要将焦点设置在自身上。

                          需要执行业务逻辑来确定给定字段是否是设置焦点的正确字段。这未显示,因为它取决于单个应用程序。

                          提交表单时,该事件需要将全局焦点标志resetFocus 设置为true。然后当每个组件更新自己时,它会看到它应该检查是否获得焦点,如果获得焦点,则调度事件以重置焦点,这样其他元素就不必继续检查。

                          编辑 附带说明一下,我将业务逻辑放在一个“实用程序”文件中,我只是导出了该方法并在每个 shouldfocus() 方法中调用它。

                          干杯!

                          【讨论】:

                          【解决方案23】:

                          React 文档现在为此提供了一个部分。 https://facebook.github.io/react/docs/more-about-refs.html#the-ref-callback-attribute

                           render: function() {
                            return (
                              <TextInput
                                ref={function(input) {
                                  if (input != null) {
                                    input.focus();
                                  }
                                }} />
                              );
                            },
                          

                          【讨论】:

                          • 我认为这是针对这种特殊情况的好方法。
                          • 我不需要在挂载时autofocus,只是在寻找输入值时保持焦点的元素。这非常适合这种情况。 (使用反应 15)
                          【解决方案24】:

                          警告:ReactDOMComponent:不要访问 DOM 节点的 .getDOMNode();相反,直接使用节点。此 DOM 节点由 App 渲染。

                          应该是

                          componentDidMount: function () {
                            this.refs.nameInput.focus();
                          }
                          

                          【讨论】:

                            【解决方案25】:

                            如果你只是想在 React 中进行自动对焦,这很简单。

                            <input autoFocus type="text" />
                            

                            如果您只想知道将代码放在哪里,答案就在 componentDidMount() 中。

                            v014.3

                            componentDidMount() {
                                this.refs.linkInput.focus()
                            }
                            

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

                            在此处阅读 API 文档:https://facebook.github.io/react/docs/top-level-api.html#reactdom.finddomnode

                            【讨论】:

                            • 记得将F大写! (注意自己和他人,而不是回答者)。
                            【解决方案26】:

                            参考。 @Dave 对 @Dhiraj 回答的评论;另一种方法是在正在渲染的元素上使用 ref 属性的回调功能(在组件首次渲染之后):

                            <input ref={ function(component){ React.findDOMNode(component).focus();} } />
                            

                            More info

                            【讨论】:

                            • 当我尝试这个时,我得到了:Uncaught TypeError: Cannot read property 'focus' of null
                            • 必须对参数进行空检查,组件未挂载时为空。所以一个简单的component &amp;&amp; React.findDomNode...。在此处阅读更多信息:facebook.github.io/react/docs/…
                            【解决方案27】:

                            这不再是最佳答案。从 v0.13 开始,在某些奇怪的情况下,this.refs 可能在 componentDidMount() 运行之后才能使用。

                            只需将 autoFocus 标签添加到您的输入字段,如 FakeRainBrigand 所示。

                            【讨论】:

                            • 多个 &lt;input autofocus&gt; 字段不会很好
                            • 当然不是。每页只有一个焦点。如果你有多个自动对焦,你应该检查你的代码和意图。
                            • @Dave 的问题是关于在渲染后将焦点设置在 &lt;input&gt;
                            • 在自动对焦上,有没有办法强制 iOS 键盘也打开?
                            • @RemiSture 同样的问题。有没有人可以解决这个问题?
                            【解决方案28】:

                            您可以将该方法调用放在渲染函数中。或者生命周期方法里面,componentDidUpdate

                            【讨论】:

                            • componentDidUpdate 对我的情况有用。调用渲染后,我需要将焦点设置在特定按钮上。
                            猜你喜欢
                            • 2021-03-18
                            • 1970-01-01
                            • 2018-04-16
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            相关资源
                            最近更新 更多