¿Cómo establecer el foco en un campo de entrada después de renderizar?


629

¿Cuál es la forma de reaccionar de establecer el foco en un campo de texto en particular después de que se representa el componente?

La documentación parece sugerir el uso de referencias, por ejemplo:

Establecer ref="nameInput"en mi campo de entrada en la función de renderizado, y luego llamar:

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

¿Pero a dónde debería llamar esto? He probado algunos lugares pero no puedo hacer que funcione.

Respuestas:


669

Deberías hacerlo en componentDidMounty en su refs callbacklugar. Algo como esto

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>


107
Esta es la respuesta correcta, pero no funcionó para mí, ya que mi componente no muestra nada hasta que se hace clic en otro botón. Esto significaba que ya estaba montado, así que tuve que agregar this.refs.nameInput.getDOMNode (). Focus (); en componentDidUpdate en lugar de componentDidMount.
Dave

99
¿Por qué, cuando se llama a element.focus (), ¿coloca el cursor al comienzo de la entrada? Vi este error (lo que considero un) en mi aplicación, en Chrome, en realidad en un <textarea> y ahora verificando sus demos aquí es lo mismo.
davnicwil

15
Advertencia: React.findDOMNode está en desuso. Utilice ReactDOM.findDOMNode de require ('react-dom') en su lugar.
André Pena

55
@HuwDavies Supongo que lo harías usando un atributo de devolución de llamada de referencia en el <input>elemento. Algo así como<input ref={ (component) => ReactDOM.findDOMNode(component).focus() } />
herman

55
¿Por qué no solo usamos ref = {(input) => {input.focus ()}} ? Esta solución funciona bien para mí.
HCLiu

841

La respuesta de @ Dhiraj es correcta, y para su comodidad, puede usar el accesorio de enfoque automático para que una entrada se enfoque automáticamente cuando se monta:

<input autoFocus name=...

Tenga en cuenta que en jsx es autoFocus(mayúscula F) a diferencia de html simple que no distingue entre mayúsculas y minúsculas.


95
Tenga en cuenta que en jsx es auto F ocus (F mayúscula) a diferencia del viejo html que no distingue entre mayúsculas y minúsculas.
prauchfuss

8
Muy bien, llegué aquí después de una larga búsqueda infructuosa :) FYI - Terminé usando React.DOM.input ({type: 'text', defaultValue: content, autoFocus: true, onFocus: function (e) {e.target. select ();}})
mlo55

44
Me parece que el enfoque automático solo funciona en el renderizado de la primera página. Ver codepen.io/ericandrewlewis/pen/PbgwqJ?editors=1111 la entrada debe enfocarse después de 3 segundos.
Eric Andrew Lewis

45
+1 para este método. Vale la pena mencionar que esto no solo usa un autofocusatributo no confiable HTML5 , sino que también lo usa focus()en el montaje DOM, porreact-dom lo que es bastante confiable.
Aaron Beall

3
No solo "por conveniencia" sino también si su componente es funcional.
phillyslick 01 de

167

A partir de React 0.15 , el método más conciso es:

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

55
Esto también maneja los escenarios fuera del render inicial, mientras que el uso de autoFocus no lo hace.
Matt Stannett

pregunta, ¿cuándo la entrada sería falsa? Me refiero a la expresión dentro de la función de flecha.
JaeGeeTee

2
@JaeGeeTee es nulo hasta que el componente se monte y / o después de que se haya desmontado (no recuerdo con seguridad cuál es el caso).
Ilya Semenov el

12
El único problema con esto es que enfoca la entrada en cualquier re-renderizado que podría no ser deseado ...
Jaroslav Benc

No funciona en mi caso (usando el componente de entrada Ant Design )
vsync

119

Centrarse en el monte

Si solo desea enfocar un elemento cuando se monta (inicialmente se procesa), se hará un uso simple del atributo autoFocus.

<input type="text" autoFocus />

Enfoque dinámico

para controlar el enfoque dinámicamente, use una función general para ocultar los detalles de implementación de sus componentes.

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

Demo completa

Reaccionar 16.3 + Componentes de clase - utilizarFocus

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

Demo completa


2
Esta respuesta contiene el enfoque correcto para React Hooks. ¡Súper! No escribe la verificación tal como está en TypeScript, sino una forma (fea) de hacerlo funcionar: (1) (htmlElRef.current as any).focus()y (2) en return {htmlElRef, setFocus}lugar de matriz.
Ahmed Fasih

@AhmedFasih, soy consciente de lo que estás diciendo, pero creo que está fuera del alcance de este hilo. Si devuelve un objeto, hace que sea más difícil controlar el nombre de la variable, lo que podría ser un problema si desea utilizarlo useFocuspara más de un elemento.
Ben Carp

8
Aquí está useFocusescrito en mecanografiado. gist.github.com/carpben/de968e377cbac0ffbdefe1ab56237573
Ben Carp

2
Súper jugoso, gracias !!! Wow, no sabía sobre las afirmaciones constantes (que as consttienes), ¡muy educativo!
Ahmed Fasih

@BenCarp Pequeña sugerencia para ganchos, podría ser mejor poner setla segunda posición como const [inputRef, setInputFocus] = useFocus(). Esto coincide con useState más. Primero el objeto, luego el armador de ese objeto
Rubanov

59

Si solo desea realizar el enfoque automático en React, es simple.

<input autoFocus type="text" />

Mientras que si solo quiere saber dónde colocar ese código, la respuesta está en componentDidMount ().

v014.3

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

En la mayoría de los casos, puede adjuntar una referencia al nodo DOM y evitar el uso de findDOMNode.

Lea los documentos de la API aquí: https://facebook.github.io/react/docs/top-level-api.html#reactdom.finddomnode


99
¡Y recuerda capitalizar eso F! (Nota para sí mismo y para los demás, no para el que responde).
2540625

29

React 16.3 agregó una nueva forma conveniente de manejar esto mediante la creación de una referencia en el constructor del componente y usarlo como a continuación:

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 más detalles, puede consultar este artículo en el blog React.

Actualizar:

A partir de React 16.8 , useRefhook se puede usar en componentes de función para lograr el mismo resultado:

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

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

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

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

26

Acabo de encontrarme con este problema y estoy usando react 15.0.1 15.0.2 y estoy usando la sintaxis ES6 y no obtuve lo que necesitaba de las otras respuestas ya que v.15 cayó hace algunas semanas y algunas de las this.refspropiedades fueron desaprobados y eliminados .

En general, lo que necesitaba era:

  1. Enfoca el primer elemento de entrada (campo) cuando se monta el componente
  2. Enfoque el primer elemento de entrada (campo) con un error (después de enviar)

Estoy usando:

  • Contenedor de reacción / Componente de presentación
  • Redux
  • React-Router

Enfoca el primer elemento de entrada

Solía autoFocus={true}en el primero<input /> en la página para que cuando se monte el componente, se enfoque.

Enfoque el primer elemento de entrada con un error

Esto tomó más tiempo y fue más complicado. Estoy manteniendo fuera el código que no es relevante para la solución por brevedad.

Tienda Redux / Estado

Necesito un estado global para saber si debo establecer el enfoque y deshabilitarlo cuando se configuró, por lo que no vuelvo a configurar el enfoque cuando los componentes se vuelven a procesar (lo usaré componentDidUpdate()para verificar la configuración del enfoque. )

Esto podría diseñarse como mejor le parezca para su aplicación.

{
    form: {
        resetFocus: false,
    }
}

Componente contenedor

El componente necesitará tener la resetfocuspropiedad establecida y un callBack para borrar la propiedad si termina estableciendo el foco en sí mismo.

También tenga en cuenta que organicé mis Action Creators en archivos separados principalmente debido a que mi proyecto es bastante grande y quería dividirlos en trozos más manejables.

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 presentación

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
}

Visión general

La idea general es que cada campo de formulario que podría tener un error y estar enfocado necesita verificarse a sí mismo y si necesita establecer el foco en sí mismo.

Hay una lógica de negocios que debe suceder para determinar si el campo dado es el campo correcto para establecer el foco. Esto no se muestra porque dependerá de la aplicación individual.

Cuando se envía un formulario, ese evento debe establecer el indicador de enfoque global resetFocusen verdadero. Luego, a medida que cada componente se actualice, verá que debe verificar para ver si obtiene el foco y, si lo hace, enviar el evento para restablecer el foco para que otros elementos no tengan que seguir revisando.

editar Como nota al margen, tenía mi lógica de negocios en un archivo de "utilidades" y simplemente exporté el método y lo llamé dentro de cadashouldfocus() método.

¡Salud!


25

Los documentos de React ahora tienen una sección para esto. 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();
        }
      }} />
    );
  },

1
Creo que esta es una buena manera de hacerlo para este escenario particular.
fabio.sussetto

No necesitaba autofocusmontar, solo buscaba que el elemento permaneciera enfocado al ingresar un valor. Esto funcionó perfectamente para ese escenario. (usando react 15)
Matt Parrilla

13

Esta ya no es la mejor respuesta. A partir de v0.13, this.refspuede no estar disponible hasta DESPUÉS de componentDidMount()ejecutarse, en algunos casos extraños.

Simplemente agregue la autoFocusetiqueta a su campo de entrada, como FakeRainBrigand mostró arriba.


44
Varios <input autofocus>campos no se comportarán bien
ᆼ ᆺ ᆼ

44
Por supuesto no. Solo un foco por página. Si tiene múltiples enfoques automáticos, debe verificar su código e intenciones.
GAEfan

2
@ Pregunta de Dave era sobre la configuración de enfoque en un <input>después de hacer
ᆼ ᆺ ᆼ

1
En el enfoque automático, ¿hay alguna manera de forzar también la apertura del teclado iOS?
Remi Sture

1
@RemiSture las mismas preguntas. ¿Alguien tiene una solución a este problema?
Nam Lê Quý

12

Árbitro. Comentario de @ Dave sobre la respuesta de @ Dhiraj; una alternativa es utilizar la funcionalidad de devolución de llamada del atributo ref en el elemento que se está representando (después de que un componente se procesa primero):

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

Más información


Cuando probé esto, obtuve:Uncaught TypeError: Cannot read property 'focus' of null
reectrix

1
Debe verificar nulo el parámetro, será nulo cuando el componente no esté montado. Así de simple component && React.findDomNode.... Lea más sobre esto aquí: facebook.github.io/react/docs/…
Por Wiklander

12

Esta es la forma correcta, cómo enfocar automáticamente. Cuando usa la devolución de llamada en lugar de la cadena como valor de referencia, se llama automáticamente. Tienes tu referencia disponible que sin la necesidad de tocar el DOM usandogetDOMNode

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

2
¿Qué pasa con una forma controlada?
pixel 67 el

@ pixel67 también. Puede establecer referencias en elementos, pero también en componentes. Pero debes ser consciente de eso cuando trabajes con él. Por lo tanto, no intentará acceder al valor de entrada, si establece la referencia en React.Component, que envuelve la entrada html.
Pavel Hasala

10

Tenga en cuenta que ninguna de estas respuestas funcionó para mí con un componente material-ui TextField . Por ¿Cómo establecer el foco en un materialUI TextField? Tuve que saltar algunos aros para que esto funcione:

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

return (
  <TextField
    hintText="Username"
    floatingLabelText="Username"
    ref={focusUsernameInputField}
  />
);

2
Parece que si su componente está animando, la llamada focus()a debe retrasarse hasta el final de la animación.
adriendenat

9

Puede poner esa llamada al método dentro de la función de renderizado. O dentro del método del ciclo de vida,componentDidUpdate


1
componentDidUpdate es lo que funcionó para mi caso. Necesitaba establecer el foco en un botón en particular después de que se llama render.
FariaC

8

No necesitas getInputDOMNode?? en este caso...

Simplemente obtenga el refy focus()cuando el componente se monte - 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'));

5

AutoFocus funcionó mejor para mí. Necesitaba cambiar algo de texto a una entrada con ese texto en doble clic, así que esto es lo que terminé con:

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

NOTA: Para solucionar el problema donde React coloca el cursor al comienzo del texto, use este método:

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

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


3

Tengo el mismo problema pero también tengo algo de animación, por lo que mi colega sugiere usar window.requestAnimationFrame

Este es el atributo ref de mi elemento:

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

2

Advertencia: ReactDOMComponent: No acceda a .getDOMNode () de un nodo DOM; en su lugar, use el nodo directamente. Este nodo DOM fue procesado por App.

Debiera ser

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

2

La respuesta más simple es agregar ref = "algún nombre" en el elemento de texto de entrada y llamar a la siguiente función.

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

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

2

Para mover el foco a un elemento recién creado, puede almacenar el ID del elemento en el estado y usarlo para establecerlo autoFocus. p.ej

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

De esta manera, ninguno de los cuadros de texto se enfoca en la carga de la página (como quiero), pero cuando presiona el botón "Agregar" para crear un nuevo registro, ese nuevo registro se enfoca.

Como autoFocusno se "ejecuta" de nuevo a menos que el componente se vuelva a montar, no tengo que molestarme en desarmarlo this.state.focus(es decir, no seguirá robando el foco mientras actualizo otros estados).


1

Leí casi toda la respuesta pero no vi un getRenderedComponent().props.input

Configure sus referencias de entrada de texto

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


Aclare aún más su respuesta en el contexto de su código.
Jimmy Smith

1

Después de probar muchas de las opciones anteriores sin éxito, descubrí que era como era disablingy luego enablingla entrada que hizo que se perdiera el foco.

Tenía un accesorio sendingAnswerque deshabilitaría la entrada mientras estaba sondeando el 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.
/>

Una vez que eliminé el accesorio deshabilitado, todo comenzó a funcionar nuevamente.


0

Versión actualizada puedes consultar aquí

componentDidMount() {

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

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

0

Dado que hay muchas razones para este error, pensé que también publicaría el problema que estaba enfrentando. Para mí, el problema era que representaba mis entradas como contenido de otro 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>
  );
}

Así es como llamé al componente anterior:

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

0

De acuerdo con la sintaxis actualizada, puede usar this.myRref.current.focus()

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.