Comment définir le focus sur un champ de saisie après le rendu?

629

Quelle est la manière la plus efficace de définir le focus sur un champ de texte particulier après le rendu du composant?

La documentation semble suggérer l'utilisation de références, par exemple:

Définissez ref="nameInput"sur mon champ de saisie dans la fonction de rendu, puis appelez:

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

Mais où dois-je appeler cela? J'ai essayé quelques endroits mais je n'arrive pas à le faire fonctionner.

Dave
la source

Réponses:

669

Vous devriez le faire dans componentDidMountet au refs callbacklieu. Quelque chose comme ça

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
la source
107
C'est la bonne réponse, mais cela n'a pas fonctionné pour moi car mon composant ne rend d'abord rien, jusqu'à ce qu'un autre bouton soit cliqué. Cela signifiait qu'il était déjà monté, j'ai donc dû ajouter this.refs.nameInput.getDOMNode (). Focus (); dans componentDidUpdate au lieu de componentDidMount.
Dave
9
Pourquoi, lorsque element.focus () est appelé, place-t-il le curseur au début de l'entrée? J'ai vu ce (ce que je considère comme un) bogue dans mon application, en chrome, en fait dans une <textarea> et maintenant en vérifiant vos démos ici, c'est la même chose.
davnicwil
15
Avertissement: React.findDOMNode est obsolète. Veuillez utiliser ReactDOM.findDOMNode à partir de require ('react-dom') à la place.
André Pena
5
@HuwDavies Je suppose que vous le feriez en utilisant un attribut de rappel ref sur l' <input>élément. Quelque chose comme<input ref={ (component) => ReactDOM.findDOMNode(component).focus() } />
herman
5
Pourquoi ne pas simplement utiliser ref = {(input) => {input.focus ()}} ? Cette solution fonctionne bien pour moi.
HCLiu
842

@ La réponse de Dhiraj est correcte, et pour plus de commodité, vous pouvez utiliser l'accessoire autoFocus pour qu'une entrée se concentre automatiquement lorsqu'elle est montée:

<input autoFocus name=...

Notez que dans jsx c'est autoFocus(F majuscule) contrairement au vieux HTML simple qui est insensible à la casse.

Brigand
la source
95
Notez que dans jsx, son F ocus automatique (F majuscule) contrairement au vieux HTML simple qui est insensible à la casse.
prauchfuss
8
Très bien, je suis arrivé ici après une longue recherche infructueuse :) FYI - J'ai fini par utiliser React.DOM.input ({type: 'text', defaultValue: content, autoFocus: true, onFocus: function (e) {e.target. select ();}})
mlo55
4
Je trouve que l'autofocus ne fonctionne que sur le rendu de première page. Voir codepen.io/ericandrewlewis/pen/PbgwqJ?editors=1111 l'entrée doit être focalisée après 3 secondes.
Eric Andrew Lewis
45
+1 pour cette méthode. Il convient de mentionner que cela n'utilise pas seulement un autofocusattribut non fiable HTML5 , il utilise enfocus()react-dom fait sur le montage DOM , il est donc assez fiable.
Aaron Beall
3
Non seulement "pour plus de commodité", mais aussi si votre composant est un composant fonctionnel.
phillyslick
167

Depuis React 0.15 , la méthode la plus concise est:

<input ref={input => input && input.focus()}/>
Ilya Semenov
la source
5
Cela gère également les scénarios en dehors du rendu initial, contrairement à l'utilisation de la mise au point automatique.
Matt Stannett
question, quand la saisie serait-elle fausse? Je fais référence à l'expression à l'intérieur de la fonction flèche.
JaeGeeTee
2
@JaeGeeTee est nul jusqu'à ce que le composant soit monté et / ou après qu'il ait été démonté (je ne me souviens pas avec certitude quel est le cas).
Ilya Semenov
12
Le seul problème avec cela est qu'il concentre l'entrée sur tout re-rendu qui pourrait ne pas être souhaité ..
Jaroslav Benc
Ne fonctionne pas dans mon cas (en utilisant le composant d'entrée Ant Design )
vsync
119

Focus sur le montage

Si vous souhaitez simplement focaliser un élément lors de son montage (rendu initial), une simple utilisation de l'attribut autoFocus fera l'affaire.

<input type="text" autoFocus />

Focus dynamique

pour contrôler le focus de manière dynamique, utilisez une fonction générale pour masquer les détails d'implémentation de vos composants.

React 16.8 + Composant fonctionnel - 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 ] 
}

Démo complète

Composants de classe React 16.3 + - useFocus

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

Démo complète

Ben Carp
la source
2
Cette réponse contient la bonne approche pour React Hooks. Super! Il ne vérifie pas le type tel quel dans TypeScript mais une façon (laide) de le faire fonctionner: (1) (htmlElRef.current as any).focus()et (2) return {htmlElRef, setFocus}au lieu du tableau.
Ahmed Fasih
@AhmedFasih, je suis conscient de ce que vous dites, mais je pense que c'est hors de portée pour ce fil. Si vous renvoyez un objet, il est plus difficile de contrôler le nom de la variable, ce qui pourrait être un problème si vous souhaitez utiliser useFocusplusieurs éléments.
Ben Carp
8
Voici useFocusécrit en Typescript. gist.github.com/carpben/de968e377cbac0ffbdefe1ab56237573
Ben Carp
2
Super juteux, merci !!! Wow, je ne connaissais pas les affirmations const (que as constvous avez), très pédagogiques!
Ahmed Fasih
@BenCarp Petite suggestion pour les crochets, pourrait être préférable de mettre le setdans la deuxième position comme const [inputRef, setInputFocus] = useFocus(). Cela correspond à useState more. D'abord l'objet, puis le passeur de cet objet
Rubanov
59

Si vous souhaitez simplement effectuer la mise au point automatique dans React, c'est simple.

<input autoFocus type="text" />

Alors que si vous voulez juste savoir où mettre ce code, la réponse se trouve dans componentDidMount ().

v014.3

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

Dans la plupart des cas, vous pouvez attacher une référence au nœud DOM et éviter d'utiliser du tout findDOMNode.

Lisez les documents de l'API ici: https://facebook.github.io/react/docs/top-level-api.html#reactdom.finddomnode

Jack Lee
la source
9
Et n'oubliez pas de capitaliser cela F! (Note à soi et aux autres, pas au répondeur).
2540625
29

React 16.3 a ajouté un nouveau moyen pratique de gérer cela en créant une référence dans le constructeur du composant et en l'utilisant comme ci-dessous:

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

Pour plus de détails, vous pouvez consulter cet article sur le blog React.

Mise à jour:

À partir de React 16.8 , le useRefcrochet peut être utilisé dans les composants de fonction pour obtenir le même résultat:

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

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

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

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

Je viens de rencontrer ce problème et j'utilise react 15.0.1 15.0.2 et j'utilise la syntaxe ES6 et je n'ai pas tout à fait obtenu ce dont j'avais besoin des autres réponses depuis la version 15 supprimée il y a des semaines et certaines des this.refspropriétés ont été dépréciés et retirés .

En général, ce dont j'avais besoin était:

  1. Concentrez le premier élément d'entrée (champ) lorsque le composant est monté
  2. Concentrez le premier élément d'entrée (champ) avec une erreur (après la soumission)

J'utilise:

  • Conteneur React / Composant de présentation
  • Redux
  • React-Router

Focus sur le premier élément d'entrée

J'ai utilisé autoFocus={true}le premier<input /> sur la page de sorte que lorsque le composant sera monté, il se concentrera.

Concentrez le premier élément d'entrée avec une erreur

Cela a pris plus de temps et était plus compliqué. Je garde le code qui n'est pas pertinent pour la solution par souci de concision.

Magasin / état Redux

J'ai besoin d'un état global pour savoir si je dois définir le focus et le désactiver quand il a été défini, donc je ne continue pas à redéfinir le focus lorsque les composants sont rendus (je vais utiliser componentDidUpdate() pour vérifier le réglage du focus. )

Cela pourrait être conçu comme bon vous semble pour votre application.

{
    form: {
        resetFocus: false,
    }
}

Composant de conteneur

Le composant devra avoir le resetfocus propriété définie et un callBack pour effacer la propriété s'il finit par définir le focus sur lui-même.

Notez également que j'ai organisé mes créateurs d'action dans des fichiers séparés, principalement en raison de mon projet est assez volumineux et je voulais les diviser en morceaux plus gérables.

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

Composant de présentation

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
}

Aperçu

L'idée générale est que chaque champ de formulaire qui pourrait avoir une erreur et être concentré doit se vérifier lui-même et s'il doit se concentrer sur lui-même.

Il y a une logique métier qui doit se produire pour déterminer si le champ donné est le bon champ pour définir le focus. Cela ne s'affiche pas car cela dépendra de l'application individuelle.

Lorsqu'un formulaire est soumis, cet événement doit définir l'indicateur de focus global resetFocussur true. Puis, au fur et à mesure que chaque composant se met à jour, il verra qu'il doit vérifier s'il obtient le focus et si c'est le cas, répartir l'événement pour réinitialiser le focus afin que les autres éléments n'aient pas à continuer de vérifier.

edit En remarque, j'avais ma logique métier dans un fichier "utilitaires" et je viens d'exporter la méthode et de l'appeler dans chaqueshouldfocus() méthode.

À votre santé!

jmbertucci
la source
25

Les documents React ont maintenant une section pour cela. 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
la source
1
Je pense que c'est une bonne façon de le faire pour ce scénario particulier.
fabio.sussetto
Je n'avais pas besoin de autofocusmonter, je cherchais simplement l'élément pour rester concentré lors de la saisie d'une valeur. Cela a parfaitement fonctionné pour ce scénario. (en utilisant React 15)
Matt Parrilla
13

Ce n'est plus la meilleure réponse. À partir de la v0.13, this.refspeut ne pas être disponible avant APRÈScomponentDidMount() exécution, dans certains cas étranges.

Ajoutez simplement la autoFocusbalise à votre champ de saisie, comme FakeRainBrigand l'a montré ci-dessus.

GAEfan
la source
4
Plusieurs <input autofocus>champs ne se comporteront pas bien
ᆼ ᆺ ᆼ
4
Bien sûr que non. Un seul focus par page. Si vous disposez de plusieurs autofocus, vous devez vérifier votre code et vos intentions.
GAEfan
2
@ La question de Dave était de mettre l'accent sur un <input>rendu après
ᆺ ᆼ
1
Sur l'autofocus, existe-t-il un moyen de forcer le clavier iOS à s'ouvrir également?
Remi Sture
1
@RemiSture mêmes questions. Quelqu'un at-il une solution à ce problème?
Nam Lê Quý
12

Réf. Commentaire de @ Dave sur la réponse de @ Dhiraj; une alternative consiste à utiliser la fonctionnalité de rappel de l'attribut ref sur l'élément en cours de rendu (après le premier rendu d'un composant):

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

Plus d'informations

o01
la source
Quand j'ai essayé cela, j'ai eu:Uncaught TypeError: Cannot read property 'focus' of null
reectrix le
1
Vous devez annuler la vérification du paramètre, il sera nul lorsque le composant n'est pas monté. Donc, c'est simple component && React.findDomNode.... En savoir plus à ce sujet ici: facebook.github.io/react/docs/…
Par Wiklander
12

C'est la bonne façon, la mise au point automatique. Lorsque vous utilisez le rappel au lieu de la chaîne comme valeur de référence, il est automatiquement appelé. Vous avez obtenu votre référence disponible sans avoir besoin de toucher le DOM en utilisantgetDOMNode

render: function() {
  return <TextInput ref={(c) => this._input = c} />;
},
componentDidMount: function() {
  this._input.focus();
},
Pavel Hasala
la source
2
qu'en est-il d'une forme contrôlée?
pixel 67
@ pixel67 Aussi. Vous pouvez définir une référence sur les éléments, mais également sur les composants. Mais vous devez en être conscient lorsque vous travaillez avec. Donc, vous n'essaierez pas d'accéder à .value of input, si vous définissez la référence sur React.Component, qui encapsule l'entrée html.
Pavel Hasala
10

Notez qu'aucune de ces réponses n'a fonctionné pour moi avec un composant MaterialField TextField . Par Comment définir le focus sur un MaterialUI TextField? J'ai dû sauter à travers quelques cerceaux pour que cela fonctionne:

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

return (
  <TextField
    hintText="Username"
    floatingLabelText="Username"
    ref={focusUsernameInputField}
  />
);
Lane Rettig
la source
2
Il semble que si votre composant est en train d’animer, l’appel à focus()doit être retardé jusqu’à la fin de l’animation.
adriendenat
9

Vous pouvez placer cet appel de méthode dans la fonction de rendu. Ou à l'intérieur de la méthode du cycle de vie,componentDidUpdate

Raza Gill
la source
1
componentDidUpdate est ce qui a fonctionné pour mon cas. J'avais besoin de mettre le focus sur un bouton particulier après l'appel du rendu.
FariaC
8

Vous n'avez pas besoin getInputDOMNode?? dans ce cas...

Obtenez simplement le refet focus()quand le composant est monté - 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
la source
5

La mise au point automatique a fonctionné le mieux pour moi. J'avais besoin de changer du texte en une entrée avec ce texte lors d'un double clic, c'est donc ce que j'ai fini avec:

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

REMARQUE: pour résoudre le problème où React place le curseur au début du texte, utilisez cette méthode:

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

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

Luke Watts
la source
3

J'ai le même problème mais j'ai aussi une animation, donc mon collègue suggère d'utiliser window.requestAnimationFrame

c'est l'attribut ref de mon élément:

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

Avertissement: ReactDOMComponent: n'accédez pas à .getDOMNode () d'un nœud DOM; utilisez plutôt le nœud directement. Ce nœud DOM a été rendu par App.

Devrait être

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

La réponse la plus simple est d'ajouter le ref = "un nom" dans l'élément de texte d'entrée et d'appeler la fonction ci-dessous.

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

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

Pour déplacer le focus vers un élément nouvellement créé, vous pouvez stocker l'ID de l'élément dans l'état et l'utiliser pour définir autoFocus. par exemple

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 cette façon, aucune des zones de texte ne se concentre sur le chargement de la page (comme je le souhaite), mais lorsque vous appuyez sur le bouton "Ajouter" pour créer un nouvel enregistrement, ce nouvel enregistrement obtient le focus.

Étant donné autoFocusque "ne s'exécute" plus à moins que le composant ne soit remonté, je n'ai pas besoin de déranger this.state.focus(c'est-à-dire qu'il ne continuera pas à voler le focus lorsque je mettrai à jour d'autres états).

mpen
la source
1

Lisez presque toute la réponse mais n'en voyez pas getRenderedComponent().props.input

Définissez vos références de saisie de texte

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

nichery
la source
Veuillez clarifier davantage votre réponse dans le contexte de leur code.
Jimmy Smith
1

Après avoir essayé beaucoup d'options ci-dessus sans succès, j'ai constaté que c'était comme je l'étais disabling, puis enablingl'entrée qui a causé la perte de la concentration.

J'avais un accessoire sendingAnswerqui désactiverait l'entrée pendant que je sondais le 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.
/>

Une fois que j'ai supprimé l'accessoire désactivé, tout a recommencé à fonctionner.

Jack
la source
0

Version mise à jour que vous pouvez consulter ici

componentDidMount() {

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

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

Comme il y a beaucoup de raisons à cette erreur, j'ai pensé que je publierais également le problème auquel j'étais confronté. Pour moi, le problème est que j'ai rendu mes entrées en tant que contenu d'un autre composant.

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

Voici comment j'ai appelé le composant ci-dessus:

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

Selon la syntaxe mise à jour, vous pouvez utiliser this.myRref.current.focus()

Arun Kumar
la source