web-dev-qa-db-fra.com

Définir le focus sur l'entrée après le rendu

Quel est le moyen de réactiver 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ù devrais-je appeler cela? J'ai essayé quelques endroits mais je n'arrive pas à le faire fonctionner.

523
Dave

Vous devriez le faire dans componentDidMount et refs callback . 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>_
583
Dhiraj

La réponse de @ Dhiraj est correcte et, pour plus de commodité, vous pouvez utiliser l'autofocus pour qu'une entrée soit automatiquement mise au point lorsqu'elle est montée:

<input autoFocus name=...

Notez que dans jsx, il s’agit de autoFocus (F majuscule), contrairement à HTML classique qui n’est pas sensible à la casse.

722
Brigand

À partir de React 0.15, la méthode la plus concise est la suivante:

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

Si vous voulez juste faire l'autofocus dans React, c'est simple.

<input autoFocus type="text" />

Tandis que si vous voulez juste savoir où mettre ce code, answer est dans composantDidMount ().

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

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

51
Jack Lee

Focus sur le montage

Si vous voulez simplement focaliser un élément quand il monte (rend initialement), une simple utilisation de l'attribut autoFocus fera l'affaire.

<input type="text" autoFocus />

Focus interactif

pour contrôler la mise au point de manière conditionnelle, utilisez la composition de fonction pour masquer les détails de mise en œuvre de dom et de navigateur de vos composants.

React 16.8 + Composant fonctionnel - crochet hook useFocus

const useFocus = () => {
    const htmlElRef = useRef(null)
    const setFocus = () => {htmlElRef.current &&  htmlElRef.current.focus()}

    return [ setFocus, htmlElRef ] 
}

Ensuite, utilisez-le dans n'importe quel composant fonctionnel.

const FocusDemo = () => {

    const [value, setValue] = useState("")
    const [setInputFocus, inputRef] = useFocus()
    const [setBtnFocus, btnRef] = useFocus()

    return (
        <> {/* React.Fragment */}

            <input
                value={value}
                onChange={(e)=>{
                    setValue(e.target.value)
                    if ( some logic ){ setBtnFocus() }
                }}
                ref={inputRef}
            />

            <button 
               onClick={setInputFocus}
               ref={btnRef}
            >
               Refocus
            </button>

        </>
    )

}

démo

Composants React 16.3 + Class - useizeFocus

const utilizeFocus = () => {
    const ref = React.createRef()
    const setFocus = () => {ref.current &&  ref.current.focus()}

    return {setFocus, ref} 
}

Puis utilisez-le dans n'importe quel composant de classe

class App extends Component {
  constructor(props){
    super(props)
    this.input1Focus = utilizeFocus()
    this.BtnFocus = utilizeFocus()
  }

  render(){
    return (
      <> {/* React.Fragment */}

          <input
              value={value}
              onChange={(e)=>{
                  setValue(e.target.value)
                  if ( some logic ){ this.BtnFocus.setFocus() }
              }}
              ref={this.input1Focus.ref}
          />

          <button 
             onClick={this.input1Focus.setFocus()}
             ref={this.BtnFocus.ref}
          >
             Refocus
          </button>

      </>
    )
  } 
}

démo

46
Ben Carp

Je viens de rencontrer ce problème et je me sers de réagir 15.0.1 15.0.2 et j’utilise la syntaxe ES6 et n’ai pas tout à fait obtenu ce dont j’avais besoin des autres réponses depuis que v.15 a été abandonné il ya plusieurs semaines et que certaines des propriétés this.refs sont obsolètes et supprimé .

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

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

J'utilise:

  • Réactif Conteneur/Composant De Présentation
  • Redux
  • React-Router

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

J'ai utilisé autoFocus={true} sur le premier <input /> de la page afin que, lorsque le composant est monté, il obtienne le focus.

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

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

Redux Store/Etat

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, pour ne pas continuer à le redéfinir lorsque les composants sont restitués (je vais utiliser componentDidUpdate() pour vérifier la mise au point.)

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

{
    form: {
        resetFocus: false,
    }
}

Composant conteneur

Le composant doit avoir la propriété resetfocus 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 la taille relativement importante de mon projet et que je souhaitais les scinder en plusieurs morceaux plus faciles à gérer.

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
}

Vue d'ensemble

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

Il faut qu'il y ait une logique métier pour déterminer si le champ donné est le bon champ sur lequel se concentrer. Cela ne s'affiche pas car cela dépend de l'application individuelle.

Lorsqu'un formulaire est soumis, cet événement doit définir l'indicateur de focus global resetFocus sur true. Ensuite, chaque composant se mettant à jour, il verra qu'il doit vérifier s'il obtient le focus et, le cas échéant, envoie l'événement pour le réinitialiser afin que les autres éléments n'aient pas à vérifier.

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

À votre santé!

25
jmbertucci

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();
        }
      }} />
    );
  },
23
Kevin Suttle

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 vérifier cet article dans React blog.

Mise à jour:

À partir de React 16.8 , useRef hook peut être utilisé dans les composants de la 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>
  );
};
18
ettanany

C’est la bonne façon de faire la mise au point automatique. Lorsque vous utilisez callback au lieu de chaîne comme valeur de référence, celle-ci est automatiquement appelée. Vous avez votre référence disponible que sans avoir besoin de toucher le DOM avec getDOMNode

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

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 du composant):

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

Plus d'infos

11
o01

Ce n'est plus la meilleure réponse. À partir de la v0.13, il est possible que this.refs ne soit pas disponible avant l'exécution de AFTER componentDidMount(), dans certains cas étranges.

Ajoutez simplement la balise autoFocus à votre champ de saisie, comme le montre FakeRainBrigand ci-dessus.

10
GAEfan

Vous pouvez mettre cet appel de méthode dans la fonction de rendu. Ou dans la méthode du cycle de vie, componentDidUpdate

8
Raza Gill

Notez qu'aucune de ces réponses n'a fonctionné pour moi avec un composant TextField matériel-ui . Per Comment définir le focus sur un materialUI TextField? Je devais 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}
  />
);
8
Lane Rettig

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

Obtenez simplement les ref et focus() quand le composant est monté - composantDidMount ...

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

AutoFocus a fonctionné mieux pour moi. J'avais besoin de changer du texte en une entrée avec ce texte en double-clic donc c'est 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 signe d'insertion 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

5
Luke Watts

J'ai le même problème mais j'ai aussi une animation, alors 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()})}}
3
SHI1485

La réponse la plus simple consiste à ajouter le ref = "un nom" dans l'élément de texte de saisie et à appeler la fonction ci-dessous.

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

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

Avertissement: ReactDOMComponent: n'accédez pas à .getDOMNode () d'un noeud 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();
}
2
Kirk Strobeck

Pour déplacer le focus sur 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, celui-ci devient actif.

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

1
mpen

Après avoir essayé beaucoup d’options ci-dessus sans succès, j’ai trouvé que c’était comme j’étais disabling puis enabling l’entrée qui avait entraîné la perte du focus.

J'avais un accessoire sendingAnswer qui désactivait l'entrée pendant l'interrogation du 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 retiré l'accessoire handicapé, tout a recommencé à fonctionner.

1
Jack

Lire la quasi-totalité de la réponse mais je n'ai pas vu de getRenderedComponent().props.input

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

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

1
nichery

Version mise à jour que vous pouvez vérifier ici

componentDidMount() {

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

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