Como definir o foco em um campo de entrada após a renderização?

629

Qual é a maneira de reagir ao definir o foco em um campo de texto específico depois que o componente é renderizado?

A documentação parece sugerir o uso de referências, por exemplo:

Defina ref="nameInput"no meu campo de entrada na função renderizar e chame:

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

Mas onde devo chamar isso? Eu tentei alguns lugares, mas não consigo fazê-lo funcionar.

Dave
fonte

Respostas:

669

Você deve fazê-lo componentDidMounte em refs callbackvez disso. Algo assim

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>

Dhiraj
fonte
107
Esta é a resposta correta, mas não funcionou para mim, pois meu componente primeiro não processa nada, até que outro botão seja clicado. Isso significava que ele já estava montado, então tive que adicionar this.refs.nameInput.getDOMNode (). Focus (); em componentDidUpdate em vez de componentDidMount.
Dave
9
Por que, quando element.focus () é chamado, ele coloca o cursor no início da entrada? Eu vi esse (o que considero um) bug no meu aplicativo, no chrome, na verdade em uma <textarea> e agora verificando suas demos aqui, é a mesma coisa.
davnicwil
15
Aviso: React.findDOMNode está obsoleto. Por favor, use ReactDOM.findDOMNode de require ('react-dom').
André Pena
5
@HuwDavies Acho que você faria usando um atributo de retorno de chamada ref no <input>elemento. Algo como<input ref={ (component) => ReactDOM.findDOMNode(component).focus() } />
herman
5
Por que não usamos apenas ref = {(input) => {input.focus ()}} ? Esta solução funciona bem para mim.
precisa saber é o seguinte
841

A resposta de @ Dhiraj está correta e, por conveniência, você pode usar o suporte autoFocus para que uma entrada foque automaticamente quando montada:

<input autoFocus name=...

Observe que no jsx é autoFocus(maiúscula F) diferente do html antigo simples, que não diferencia maiúsculas de minúsculas.

Brigand
fonte
95
Observe que, em jsx, seu auto F ocus (capital F) é diferente do html antigo simples, que não diferencia maiúsculas de minúsculas.
prauchfuss
8
Muito bom, cheguei aqui depois de uma longa pesquisa infrutífera :) FYI - Acabei usando React.DOM.input ({type: 'text', defaultValue: content, autoFocus: true, onFocus: function (e) {e.target. select ();}})
mlo55 20/02
4
Acho que o foco automático funciona apenas na renderização da primeira página. Consulte codepen.io/ericandrewlewis/pen/PbgwqJ?editors=1111, a entrada deve ser focada após 3 segundos.
Eric Andrew Lewis
45
+1 para este método. Vale a pena mencionar que isso não basta usar HTML5 não confiável autofocusatributo, ele realmente usa focus()no DOM montar emreact-dom por isso é bastante confiável.
Aaron Beall
3
Não apenas "por conveniência", mas também se o seu componente é um componente funcional.
phillyslick
167

No React 0.15 , o método mais conciso é:

<input ref={input => input && input.focus()}/>
Ilya Semenov
fonte
5
Isso também lida com os cenários fora da renderização inicial, enquanto apenas o foco automático não.
Matt Stannett
pergunta, quando a entrada seria falsa? Estou me referindo à expressão dentro da função de seta.
precisa saber é o seguinte
2
@JaeGeeTee é nulo até que o componente seja montado e / ou depois de desmontado (não me lembro com certeza qual é o caso).
Ilya Semenov
12
O único problema com isso é que ele se concentra entrada em qualquer re-render o que não pode ser desejado ..
Jaroslav Benc
Não funciona no meu caso (usando Ant projeto componente de entrada)
vsync
118

Foco na montagem

Se você deseja apenas focar um elemento quando ele é montado (renderizado inicialmente), basta usar o atributo autoFocus.

<input type="text" autoFocus />

Foco dinâmico

Para controlar o foco dinamicamente, use uma função geral para ocultar os detalhes da implementação de seus componentes.

React 16.8 + Componente funcional - 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 ] 
}

Demonstração completa

React 16.3 + Componentes da classe - utilizeFocus

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

Demonstração completa

Ben Carp
fonte
2
Esta resposta contém a abordagem correta para React Hooks. Super! Ele não verifica como está no TypeScript, mas com uma maneira (feia) de fazê-lo funcionar: (1) (htmlElRef.current as any).focus()e (2) em return {htmlElRef, setFocus}vez da matriz.
Ahmed Fasih
@ AhmedFasih, estou ciente do que você está dizendo, mas acho que está fora do escopo deste segmento. Se você retornar um objeto, fica mais difícil controlar o nome da variável, o que pode ser um problema se você deseja usar useFocuspara mais de um elemento.
Ben Carp
8
Aqui está useFocusescrito em texto datilografado. gist.github.com/carpben/de968e377cbac0ffbdefe1ab56237573
Ben Carp
2
Super suculenta, obrigada !!! Uau, eu não sabia das afirmações constantes (que as constvocê tem), muito educativas!
Ahmed Fasih
@BenCarp Pequena sugestão para ganchos, pode ser melhor colocar o setna segunda posição como const [inputRef, setInputFocus] = useFocus(). Isso corresponde a useState more. Primeiro o objeto, depois o levantador desse objeto
Rubanov 02/09/19
59

Se você quiser apenas fazer o foco automático no React, é simples.

<input autoFocus type="text" />

Embora se você quiser apenas saber onde colocar esse código, a resposta está em componentDidMount ().

v014.3

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

Na maioria dos casos, você pode anexar uma referência ao nó DOM e evitar o uso de findDOMNode.

Leia os documentos da API aqui: https://facebook.github.io/react/docs/top-level-api.html#reactdom.finddomnode

Jack Lee
fonte
9
E lembre-se de capitalizar isso F! (Nota para si e para os outros, não para responder).
2540625
29

O React 16.3 adicionou uma nova maneira conveniente de lidar com isso, criando uma ref no construtor do componente e use-o como abaixo:

class MyForm extends Component {
  constructor(props) {
      super(props);

      this.textInput = React.createRef();
  }

  componentDidMount() {
    this.textInput.current.focus(); // one important change here is that we need to access the element via current.
  }

  render() {
    // instead of using arrow function, the created ref can be used directly.
    return(
      <div>
        <input ref={this.textInput} />
      </div>
    );
  }
}

Para mais detalhes, você pode conferir este artigo no blog React.

Atualizar:

A partir do React 16.8 , o useRefhook pode ser usado nos componentes de função para obter o mesmo resultado:

import React, { useEffect, useRef } from 'react';

const MyForm = () => {
  const textInput = useRef(null);

  useEffect(() => {
    textInput.current.focus();
  }, []);

  return (
    <div>
      <input ref={textInput} />
    </div>
  );
};
ettanany
fonte
26

Acabei de encontrar este problema e estou usando o react 15.0.1 15.0.2 e estou usando a sintaxe ES6 e não obtive o que precisava das outras respostas desde que a v.15 caiu semanas atrás e algumas das this.refspropriedades foram preteridos e removidos .

Em geral, o que eu precisava era:

  1. Focalize o primeiro elemento de entrada (campo) quando o componente for montado
  2. Focalize o primeiro elemento de entrada (campo) com um erro (após o envio)

Estou a usar:

  • Reagir recipiente / componente de apresentação
  • Restaurado
  • React-Router

Focar o primeiro elemento de entrada

Eu usei autoFocus={true}o primeiro <input />na página para que, quando o componente for montado, ele tenha foco.

Focar o primeiro elemento de entrada com um erro

Isso levou mais tempo e foi mais complicado. Estou mantendo fora do código que não é relevante para a solução por questões de brevidade.

Loja / Estado Redux

Eu preciso de um estado global para saber se devo definir o foco e desabilitá-lo quando foi definido, para não continuar redefinindo o foco quando os componentes forem renderizados novamente (usarei componentDidUpdate()para verificar a definição do foco. )

Isso pode ser projetado da maneira que você achar melhor para sua aplicação.

{
    form: {
        resetFocus: false,
    }
}

Componente Container

O componente precisará ter a resetfocuspropriedade definida e um callBack para limpar a propriedade, se acabar definindo o foco em si mesmo.

Observe também que organizei meus Action Creators em arquivos separados, principalmente devido ao meu projeto ser bastante grande e que eu queria dividi-los em partes mais gerenciáveis.

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

Componente de apresentação

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
}

Visão geral

A ideia geral é que cada campo de formulário que possa ter um erro e ser focado precise se verificar e se precisar definir o foco em si mesmo.

Há uma lógica comercial que precisa acontecer para determinar se o campo especificado é o campo certo para definir o foco. Isso não é mostrado porque dependerá do aplicativo individual.

Quando um formulário é enviado, esse evento precisa definir o sinalizador de foco global resetFocuscomo true. Então, à medida que cada componente se atualiza, ele verifica se obtém o foco e, se obtém, despacha o evento para redefinir o foco para que outros elementos não precisem continuar verificando.

edit Como observação, eu tinha minha lógica de negócios em um arquivo "utilitários" e acabei de exportar o método e o chamei em cada shouldfocus()método.

Felicidades!

jmbertucci
fonte
25

Os documentos do React agora têm uma seção para isso. 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();
        }
      }} />
    );
  },
Kevin Suttle
fonte
1
Eu acho que essa é uma boa maneira de fazer isso para esse cenário em particular.
Fabio.sussetto
Eu não precisava autofocusmontar, estava apenas procurando o elemento para manter o foco ao inserir um valor. Isso funcionou perfeitamente para esse cenário. (usando reagir 15)
Matt Parrilla
13

Esta não é mais a melhor resposta. A partir da v0.13, this.refspode não estar disponível até APÓS a componentDidMount()execução, em alguns casos estranhos.

Basta adicionar a autoFocustag ao seu campo de entrada, como o FakeRainBrigand mostrou acima.

GAEfan
fonte
4
Vários <input autofocus>campos não se comportarão bem
29 ᆺ ᆼ
4
Claro que não. Apenas um foco por página. Se você tiver vários focos automáticos, verifique seu código e intenções.
GAEfan
2
A pergunta de @ Dave foi sobre como definir o foco em uma <input>renderização posterior
#: 313115
1
No foco automático, existe uma maneira de forçar a abertura do teclado do iOS também?
Remi Sture 28/10
1
@RemiSture mesmas perguntas. Alguém tem uma solução para este problema?
Nam Lê Quý 16/12/19
12

Ref. Comentário de @ Dave na resposta de @ Dhiraj; uma alternativa é usar a funcionalidade de retorno de chamada do atributo ref no elemento que está sendo renderizado (depois que um componente é renderizado pela primeira vez):

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

Mais informações

o01
fonte
Quando tentei isso, obtive:Uncaught TypeError: Cannot read property 'focus' of null
reectrix
1
Você deve verificar nulo o parâmetro, ele será nulo quando o componente não estiver montado. Tão simples component && React.findDomNode.... Leia mais sobre isso aqui: facebook.github.io/react/docs/…
Por Wiklander
12

Esta é a maneira correta, como focar automaticamente. Quando você usa retorno de chamada em vez de sequência como valor ref, ele é chamado automaticamente. Você conseguiu sua referência disponível, sem a necessidade de tocar no DOM usandogetDOMNode

render: function() {
  return <TextInput ref={(c) => this._input = c} />;
},
componentDidMount: function() {
  this._input.focus();
},
Pavel Hasala
fonte
2
que tal uma forma controlada?
pixel de 67
@ pixel67 também. Você pode definir referência em elementos, mas também em componentes. Mas você deve estar ciente disso ao trabalhar com ele. Portanto, você não tentará acessar .value da entrada, se você definir a referência no React.Component, que envolve a entrada html.
Pavel Hasala
10

Observe que nenhuma dessas respostas funcionou para mim com um componente TextField da interface do usuário . Por Como definir o foco para um materialUI TextField? Eu tive que pular algumas etapas para fazer isso funcionar:

const focusUsernameInputField = input => {
  if (input) {
    setTimeout(() => {input.focus()}, 100);
  }
};

return (
  <TextField
    hintText="Username"
    floatingLabelText="Username"
    ref={focusUsernameInputField}
  />
);
Lane Rettig
fonte
2
Parece que, se o seu componente estiver animando, a chamada para focus()deve ser adiada até o final da animação.
adriendenat 10/07
9

Você pode colocar essa chamada de método dentro da função de renderização. Ou dentro do método do ciclo de vida,componentDidUpdate

Raza Gill
fonte
1
componentDidUpdate é o que funcionou para o meu caso. Eu precisava definir o foco em um botão específico após a renderização ser chamada.
FariaC 03/09/2015
8

Você não precisa getInputDOMNode? nesse caso...

Basta obter o refe focus()quando o componente é montado - 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'));
Alireza
fonte
5

O AutoFocus funcionou melhor para mim. Eu precisava alterar algum texto para uma entrada com esse texto em clique duplo, então foi assim que eu acabei:

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

NOTA: Para corrigir o problema em que o React coloca o cursor no início do texto, use este método:

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

Encontre aqui: https://coderwall.com/p/0iz_zq/how-to-put-focus-at-the-end-of-an-input-with-react-js

Luke Watts
fonte
3

Eu tenho o mesmo problema, mas também tenho algumas animações, então meu colega sugere usar window.requestAnimationFrame

este é o atributo ref do meu elemento:

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

Aviso: ReactDOMComponent: não acessa .getDOMNode () de um nó DOM; em vez disso, use o nó diretamente. Este nó DOM foi renderizado por App.

Deveria estar

componentDidMount: function () {
  this.refs.nameInput.focus();
}
Kirk Strobeck
fonte
2

A resposta mais simples é adicionar ref = "some name" no elemento de entrada de texto e chamar a função abaixo.

componentDidMount(){
   this.refs.field_name.focus();
}
// here field_name is ref name.

<input type="text" ref="field_name" />
Venkatesh Somu
fonte
2

Para mover o foco para um elemento recém-criado, você pode armazenar o ID do elemento no estado e usá-lo para definir autoFocus. por exemplo

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

Dessa forma, nenhuma das caixas de texto fica focada no carregamento da página (como eu quero), mas quando você pressiona o botão "Adicionar" para criar um novo registro, esse novo registro fica focado.

Como autoFocusnão "roda" novamente, a menos que o componente seja remontado, não preciso me preocupar em desabilitar this.state.focus(ou seja, ele não continuará roubando o foco de volta à medida que atualizo outros estados).

mpen
fonte
1

Leia quase toda a resposta, mas não viu uma getRenderedComponent().props.input

Defina suas referências de entrada de texto

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

nichery
fonte
Por favor, esclareça sua resposta no contexto de seu código.
Jimmy Smith
1

Depois de tentar muitas opções acima sem sucesso, descobri que era como era disablinge depois enablinga entrada que causou a perda do foco.

Eu tinha um suporte sendingAnswerque desabilitaria a entrada enquanto eu estava pesquisando o backend.

<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.
/>

Depois que removi o suporte desativado, tudo começou a funcionar novamente.

Jack
fonte
0

Versão atualizada você pode conferir aqui

componentDidMount() {

    // Focus to the input as html5 autofocus
    this.inputRef.focus();

}
render() {
    return <input type="text" ref={(input) => { this.inputRef = input }} />
})
Nijat Ahmadov
fonte
0

Como existem várias razões para esse erro, pensei em publicar também o problema que estava enfrentando. Para mim, o problema era que eu renderizei minhas entradas como conteúdo de outro componente.

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

Foi assim que chamei o componente acima:

<Component Content={() => {
  return (
    <input type="text"/>
  );
}} />
Dean Koštomaj
fonte
0

De acordo com a sintaxe atualizada, você pode usar this.myRref.current.focus()

Arun Kumar
fonte