如何以编程方式在React中使输入框获得焦点

54

我正在尝试实现一个非常简单的用例,即一个UI功能,在此情况下:

  1. 有一个包含一些内容的标签
  2. 如果点击它,文本输入将替换可用标签的内容
  3. 用户可以编辑内容
  4. 按下回车键后,输入将隐藏,标记将重新显示并带有更新的内容

最终,我可以正确地完成所有操作(事实上使用了MongoDB后端、Redux等),但是我唯一无法做到的事情(花费了一整天时间在Google和阅读类似的帖子中)是这样的:

当我的文本输入框出现时,我无法将焦点转移到它上!首先我试过这种方式:

<div className={((this.state.toggleWordEdit) ? '' : 'hidden')}>
<input id={this.props.word._id} className="form-control"
        ref="updateTheWord" 
        defaultValue={this.state.word}
        onChange={this.handleChange}
        onKeyPress={this.handleSubmit}
        autoFocus={this.state.toggleWordEdit}/></div>
    <div className={((this.state.toggleWordEdit) ? 'hidden' : '')}>
      <h3 onClick={this.updateWord}>
        {this.state.word}</h3>
    </div>

但是自动对焦(autoFocus)确实没有起作用 (我“猜测”是因为表单被渲染但处于隐藏状态,使自动对焦无效)。

接下来,我尝试了在我的this.updateWor中使用谷歌和S.O.F.上找到的许多建议:

this.refs.updateTheWord.focus();

还有其他类似的建议都没起作用。我还试图愚弄React,看看我能否做点什么!我使用了真实的DOM:

    const x = document.getElementById(this.props.word._id);
    x.focus();

而且它也不起作用。有一件事情,我甚至无法理解如何用言语表达建议,比如这样一个: 把ref作为方法(我“猜测”) 我甚至没试过,因为我有多个这样的组件,我需要ref进一步获取每个组件的值,而如果我的ref没有命名,我想象不出我该如何获取值!

所以您能否给我一个想法,帮助我理解,在我不使用表格(因为我需要一个单独的输入框来替换标签)的情况下,当它的CSS(Bootstrap)类失去“hidden”时,我该如何设置它的焦点?


1
这个管用吗?<input ref={c => c.focus()} /> - Brian
1
一旦输入被渲染,就会立即执行由ref定义的回调函数,并传入当前元素的引用。您可以使用此回调函数来定义对DOM节点的引用-例如ref = {c => this.refs.c = c}。 然后,您可以正常使用this.refs(只需确保先定义它)。 - Brian
7个回答

49

你使用 ref 的方式不是最佳的,或者说它已经不再是最佳实践了。尝试使用类似于以下方式的东西。

class MyClass extends React.Component {
  constructor(props) {
    super(props);
    this.focus = this.focus.bind(this);
  }

  focus() {
    this.textInput.current.focus();
  }

  render() {

    return (
      <div>
        <input
          type="text"
          ref={(input) => { this.textInput = input; }} />
        <input
          type="button"
          value="Set Focus"
          onClick={this.focus}
        />
      </div>
    );
  }
}

更新
React 16.3 开始,您可以使用 React.createRef() API。

class MyClass extends React.Component {
  constructor(props) {
    super(props);
    // create a ref to store the textInput DOM element
    this.textInput = React.createRef();
    this.focus = this.focus.bind(this);
  }

  focus() {
    // Explicitly focus the text input using the raw DOM API
    // Note: we're accessing "current" to get the DOM node
    this.textInput.current.focus();
  }

  render() {
    // tell React that we want to associate the <input> ref
    // with the `textInput` that we created in the constructor
    return (
      <div>
        <input
          type="text"
          ref={this.textInput} />
        <input
          type="button"
          value="Set Focus"
          onClick={this.focus}
        />
      </div>
    );
  }
}

React 18.xx版本开始,你可以使用useRef钩子。

import React, { useRef } from "react";

export const Form = () => {
  const inputRef = useRef(null);

  const focus = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input type="text" ref={inputRef} />
      <input type="button" value="Set Focus" onClick={focus} />
    </div>
  );
};

10
如果我有一系列输入字段,并希望在用户按下回车键时将焦点集中到下一个输入字段,我该如何动态地做到这一点? - Avi Kaminetzky
@AvremelKaminetzky 你解决了焦点问题吗?如果我正确理解你的评论,我也遇到了类似的问题。我有一个无状态的React组件InputStepper,它应该在加载时将焦点放在第一个输入上,然后在keyup之后将焦点移动到下一个InputStepper输入。但是,我看到输入焦点,然后失去焦点。当我检查元素<input ... autofocus />时,它已经设置了,但仍然无法保持焦点。这是否与React和表单/输入以及焦点有关的问题? - Rockin4Life33
@Anovative 我记不清细节了,但以下是我的(笨办法)解决方案:https://stackoverflow.com/a/46777890/4822174 - Avi Kaminetzky
如果无法正常工作,特别是对于较新的React,请尝试使用inputRef而不是ref。我可以通过编程方式聚焦TextField - ghchoi
有人用函数组件实现过吗? - Pedro Silveira

24

只需在 input 中添加 autofocus 属性即可。 (当然,在 JSX 中它是 autoFocus)

<input autoFocus ...

2
如果您想像点击一个按钮一样自动聚焦,该怎么办?! - Ahmed Elbatt
8
你可以使用布尔状态来实现,例如:<input autoFocus={state.autoFocus} ...,并在按钮点击时更改该状态。 - Mehdi Dehghani

9

useFocus hook

// General Focus Hook
const useFocus = (initialFocus = false, id = "") => {
    const [focus, setFocus] = useState(initialFocus)
    const setFocusWithTrueDefault = (param) => setFocus(isBoolean(param)? param : true)
    return ([
        setFocusWithTrueDefault, {
            autoFocus: focus,
            key: `${id}${focus}`,
            onFocus: () => setFocus(true),
            onBlur: () => setFocus(false),
        },
    ])
}


const FocusDemo = () => {

    const [labelStr, setLabelStr] = useState("Your initial Value")
    const [setFocus, focusProps] = useFocus(true)

    return (
        <> {/* React.Fragment */}
            <input
                onChange={(e)=> setLabelStr(e.target.value)}
                value={labelStr}
                {...focusProps}
            />
            <h3 onClick={setFocus}>{labelStr}</h3>
        </>
    )
    
}

要查看更完整的演示,请点击此处


1
似乎没有完整演示的链接。它在其他地方可用吗? - llamerr
2
@llamerr,今天我更喜欢一种类似但略有不同的方法:https://dev59.com/414b5IYBdhLWcg3wnS0e#54159564。从理论上讲,我更喜欢它,因为它更短,更容易理解,而且从我的经验来看,在长列表上运行得更好。请尝试一下并告诉我您的想法。如果您同意,我也会在这里更新我的解决方案。这种解决方案可能有一个演示版本,或者如果您仍然认为有必要,我可以准备一个。 - Ben Carp
这个工作了。谢谢! - Ernesto Rojas

3
除了之前的回答外,我添加了setTimeout以使其正常工作。最初的回答。
handleClick() {


    if (this.searchInput) {
        setTimeout(() => {

            this.searchInput.focus();

        }, 100);
    }
}

where searchInput is the jsx ref of the input

<input
      type="text"
      name="searchText"
      ref={(input) => { this.searchInput = input; }}
      placeholder="Search" />

handleClick()则是一个针对任何元素的onClick事件处理器


1

@BenCarp 在 TypeScript 中的回答

inputRef 传递给一个 input 元素,然后只需调用 setFocus 来设置焦点。

export const useInputFocus = (): [MutableRefObject<HTMLInputElement | undefined>, () => void] => {
  const inputRef = useRef<HTMLInputElement>();
  const setFocus = (): void => {
    const currentEl = inputRef.current;
    if (currentEl) {
      currentEl.focus();
    }
  };
  return [inputRef, setFocus];
};

0
你可以使用"useRef"钩子并引用你的输入控件,然后使用你的reference.current.focus()

1
视频不可用。 - Amy

0
使用componentDidUpdate方法来更新组件的内容。
componentDidUpdate(prevProps, prevState) {
     this.input.focus();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接