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

762

¿Cuál es la forma de reacción de establecer el enfoque en un campo de texto en particular después de que se renderiza 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, ¿dónde debería llamar a esto? Probé en algunos lugares pero no puedo hacer que funcione.

761

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>
14
  • 115
    Esta es la respuesta correcta, pero no funcionó para mí ya que mi componente primero 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 6 mar.15 a las 20:53
  • 10
    ¿Por qué, cuando se llama a element.focus (), coloca el cursor al principio de la entrada? Vi este (lo que considero un) error en mi aplicación, en Chrome, en realidad en un <textarea> y ahora revisando sus demostraciones aquí es lo mismo. davnicwil 22/10/15 a las 20:21
  • 15
    Advertencia: React.findDOMNode está obsoleto. En su lugar, utilice ReactDOM.findDOMNode de require ('react-dom'). André Pena 1 dic 2015 a las 0:43
  • 5
    @HuwDavies Supongo que lo harías usando un atributo de devolución de llamada ref en el <input>elemento. Algo así como<input ref={ (component) => ReactDOM.findDOMNode(component).focus() } />herman 29/07/2016 a las 14:12
  • 6
    ¿Por qué no usamos ref = {(input) => {input.focus ()}} ? Esta solución funciona bien para mí. H.C.Liu 15/08/18 a las 14:52
991

La respuesta de @ Dhiraj es correcta y, para mayor 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(F mayúscula) a diferencia del html antiguo, que no distingue entre mayúsculas y minúsculas.

15
  • 109
    Tenga en cuenta que en jsx su F ocus automático (F mayúscula) a diferencia del viejo html simple, que no distingue entre mayúsculas y minúsculas. prauchfuss 28/06/15 a las 13:46
  • 9
    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. seleccionar ();}})mlo55 20 de febrero de 2016 a las 15:06
  • 6
    Encuentro que el enfoque automático solo funciona en el procesamiento de la primera página. Consulte codepen.io/ericandrewlewis/pen/PbgwqJ?editors=1111, la entrada debe enfocarse después de 3 segundos. Eric Andrew Lewis 21/12/2016 a las 19:17
  • 53
    +1 para este método. Vale la pena mencionar que esto no solo usa un autofocusatributo poco confiable de HTML5 , sino que en realidad usa focus()el montaje DOM, porreact-dom lo que es bastante confiable. Aaron Beall 8 de marzo de 2017 a las 18:29
  • 4
    No solo "por conveniencia", sino también si su componente es un componente funcional. phillyslick 1 de junio de 2018 a las 16:41
229

Centrarse en la montura

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

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

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

Demostración completa

Componentes de la clase React 16.3 + - 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} 
}

Demostración completa

12
  • 3
    Esta respuesta contiene el enfoque correcto para React Hooks. ¡Súper! No se realiza una verificación de tipo tal como está en TypeScript, sino una forma (fea) de hacer que funcione: (1) (htmlElRef.current as any).focus()y (2) en return {htmlElRef, setFocus}lugar de una matriz. Ahmed Fasih 10 de agosto de 2019 a las 1:47
  • @AhmedFasih, soy consciente de lo que estás diciendo, pero creo que está fuera del alcance de este hilo. Si devuelve un objeto, será 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 10 de agosto de 2019 a las 4:47
  • 12
    Aquí está useFocusescrito en Typecript. gist.github.com/carpben/de968e377cbac0ffbdefe1ab56237573Ben Carp 10 de agosto de 2019 a las 4:47
  • 1
    @BenCarp Pequeña sugerencia para los ganchos, podría ser mejor poner el seten la segunda posición como const [inputRef, setInputFocus] = useFocus(). Esto coincide con useState más. Primero el objeto, luego el creador de ese objetoRubanov 2 de septiembre de 2019 a las 7:54
  • @Rubanov, gracias. Ajusté el código según tu sugerencia. Ben Carp 7 de septiembre de 2019 a las 8:16
199

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

<input ref={input => input && input.focus()}/>
7
  • 5
    Esto también maneja los escenarios fuera del render inicial, mientras que el uso de autoFocus no lo hace. Matt Stannett 22 de marzo de 2017 a las 2:20
  • pregunta, ¿cuándo la entrada sería falsa? Me refiero a la expresión dentro de la función de flecha. JaeGeeTee 24 de enero de 2018 a las 19:51
  • 2
    @JaeGeeTee es nulo hasta que se monta el componente y / o después de que se ha desmontado (no recuerdo con certeza cuál es el caso). Ilya Semenov 26 de enero de 2018 a las 5:51
  • dieciséis
    El único problema con esto es que se centra entrada en cualquier volver a hacer lo que no sería de desear ..Jaroslav Benc 12 mar 2018 a las 15:08
  • No funciona en mi caso (usando el componente de entrada Ant Design )vsync 16/10/19 a las 9:54
66

Si solo desea hacer un enfoque automático en React, es simple.

<input autoFocus type="text" />

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

v014.3

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

In most cases, you can attach a ref to the DOM node and avoid using findDOMNode at all.

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

1
  • 9
    ¡Y recuerde poner eso en mayúscula F! (Nota para sí mismo y para los demás, no para responder). 2540625 9 de febrero de 2016 a las 21:10
47

React 16.3 agregó una nueva forma conveniente de manejar esto creando una referencia en el constructor del componente y utilícela como se muestra a continuación:

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

Para obtener más detalles React.createRef, puede consultar este artículo en el blog de React.

Actualizar:

A partir de React 16.8 , el useRefgancho 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>
  );
};
1
  • Un pequeño cambio:textInput.current?.focus();Taras Mykhalchuk 18 de agosto a las 15:08
26

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();
        }
      }} />
    );
  },
3
  • 1
    Creo que esta es una buena forma de hacerlo para este escenario en particular. fabio.sussetto 18 de mayo de 2016 a las 18:19
  • No necesitaba autofocusmontarlo, solo buscaba que el elemento permaneciera enfocado al ingresar un valor. Esto funcionó perfectamente para ese escenario. (usando reaccionar 15)Matt Parrilla 27/06/2016 a las 14:57
  • ¡increíble! esto funciona! Mochammad Taufiq 22 dic.20 a las 7:17
26

Me acabo de encontrar con este problema y estoy usando react 15.0.1 15.0.2 y estoy usando la sintaxis de ES6 y no obtuve lo que necesitaba de las otras respuestas desde que la v.15 cayó hace semanas y algunas de las this.refspropiedades fueron obsoletos y eliminados .

En general, lo que necesitaba era:

  1. Enfocar el primer elemento de entrada (campo) cuando se monta el componente
  2. Enfocar 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

Enfocar el primer elemento de entrada

Lo usé autoFocus={true}en el primero <input />de la página para que cuando el componente se monte, se enfoque.

Enfocar el primer elemento de entrada con un error

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

Tienda / estado de Redux

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

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

{
    form: {
        resetFocus: false,
    }
}

Componente contenedor

El componente deberá 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 porque mi proyecto es bastante grande y quería dividirlos en partes 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 debe revisarse a sí mismo y si necesita enfocarse en sí mismo.

Hay una lógica empresarial que debe suceder para determinar si el campo dado es el campo correcto para establecer el enfoque. 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 si obtiene el foco y, si lo hace, enviar el evento para restablecer el foco para que otros elementos no tengan que seguir comprobando.

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 cada shouldfocus()método.

¡Salud!

1
14

Esta ya no es la mejor respuesta. A partir de la versión 0.13, es this.refsposible que no esté disponible hasta DESPUÉS de que se componentDidMount()ejecute, en algunos casos extraños.

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

5
  • 5
    Varios <input autofocus>campos no se comportarán bienᆼᆺᆼ 29 de mayo de 2015 a las 21:40 h.
  • 4
    Por supuesto no. Solo un enfoque por página. Si tiene múltiples enfoques automáticos, debe verificar su código e intenciones. GAEfan 30 de mayo de 2015 a las 17:25
  • 2
    La pregunta de @ Dave fue acerca de establecer el enfoque en un <input>renderizado posteriorᆼᆺᆼ 31 de mayo de 2015 a las 22:44
  • 1
    En el enfoque automático, ¿hay alguna forma de forzar la apertura del teclado de iOS también? Remi Sture 28/10/2016 a las 12:49
  • 1
    @RemiSture mismas preguntas. ¿Alguien tiene una solución a este problema? Nam Lê Quý 16 dic 2019 a las 8:06
13

Árbitro. Comentario de @ Dave sobre la respuesta de @ Dhiraj; una alternativa es usar la funcionalidad de devolución de llamada del atributo ref en el elemento que se representa (después de que un componente se procesa por primera vez):

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

Más información

2
  • 1
    Cuando probé esto, obtuve:Uncaught TypeError: Cannot read property 'focus' of nullreectrix 29/04/2016 a las 11:35
  • 1
    Tienes que comprobar el parámetro nulo, será nulo cuando el componente no esté montado. Tan simple component && React.findDomNode.... Lea más sobre esto aquí: facebook.github.io/react/docs/…Per Wiklander 6 de mayo de 2016 a las 8:49
12

Tenga en cuenta que ninguna de estas respuestas funcionó para mí con un componente material-ui TextField . Por cómo establecer el foco a un campo de texto materialUI? Tuve que pasar por algunos obstáculos para que esto funcionara:

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

return (
  <TextField
    hintText="Username"
    floatingLabelText="Username"
    ref={focusUsernameInputField}
  />
);
2
  • 2
    Parece que si su componente se está animando, la llamada focus()a debe retrasarse hasta el final de la animación. adriendenat 10/07/2017 a las 15:24
  • Funcionó para mí, pero usé setTimeout(() => {}, 0); solo para hacer más limpio el códigoJulian Torregrosa 11/08/20 a las 13:33
12

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

render: function() {
  return <TextInput ref={(c) => this._input = c} />;
},
componentDidMount: function() {
  this._input.focus();
},
2
  • 2
    ¿qué pasa con una forma controlada? pixel 67 13/11/2017 a las 13:48
  • @ pixel67 También. Puede establecer referencias en elementos, pero también en componentes. Pero debes tenerlo en cuenta cuando trabajes con él. Por lo tanto, no intentará acceder a .value de entrada, si establece una referencia en React.Component, que envuelve la entrada html. Pavel Hasala 12 dic 2017 a las 10:45
9

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

1
  • 1
    componentDidUpdate es lo que funcionó para mi caso. Necesitaba establecer el enfoque en un botón en particular después de llamar a render. FariaC 3 de septiembre de 2015 a las 20:02
9

Usando React Hooks / Componentes funcionales con Typescript, puede usar el useRefgancho con HTMLInputElementcomo parámetro genérico de 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>
    );
}

O si lo usa reactstrap, suministre inputReferencea en innerReflugar de 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>
    );
}
1
  • 2
    ¡Muchas gracias! HKG 2 de julio a las 16:06
8

¿No necesitas getInputDOMNode? en este caso...

Simplemente obtenga el refy focus()cuando se monte el componente - 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 un texto a una entrada con ese texto al hacer 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 en el que React coloca el signo de intercalación al principio del texto, utilice 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

4

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()})}}
3
<input type="text" autoFocus />

Siempre pruebe primero la solución simple y básica, funciona para mí.

1
  • 1
    Los elementos de enfoque automático pueden causar problemas de usabilidad tanto para los usuarios videntes como para los no videntes. No recomiendo usar esta propiedad. Ralph David Abernathy 27/07/20 a las 7:25
2

Warning: ReactDOMComponent: Do not access .getDOMNode() of a DOM node; instead, use the node directly. This DOM node was rendered by 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 función siguiente.

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

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

2

Solución simple sin enfoque automático:

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

refdesencadena el foco, y eso desencadena onFocuspara calcular el final y establecer el cursor en consecuencia.

2

Solución Ben Carp en texto mecanografiado

React 16.8 + componente funcional - 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]);
};
1

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

Establezca sus referencias de entrada de texto

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

1
  • Aclare aún más su respuesta en el contexto de su código. Jimmy Smith 10/07/2017 a las 13:17
1

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

Tenía un accesorio sendingAnswerque deshabilitaba 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 quité el accesorio deshabilitado, todo comenzó a funcionar nuevamente.

1

Según la sintaxis actualizada, puede utilizar this.myRref.current.focus()

0

Versión actualizada que puede 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 al que me enfrentaba. Para mí, el problema fue que rendericé 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>
  );
}

Esta es la forma en que llamé al componente anterior:

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