web-dev-qa-db-fra.com

React Native: Comment sélectionner le prochain TextInput après avoir appuyé sur le bouton "suivant" du clavier?

J'ai défini deux champs TextInput comme suit:

<TextInput 
   style = {styles.titleInput}
   returnKeyType = {"next"}
   autoFocus = {true}
   placeholder = "Title" />
<TextInput
   style = {styles.descriptionInput}          
   multiline = {true}
   maxLength = {200}
   placeholder = "Description" />

Mais après avoir appuyé sur le bouton "Suivant" de mon clavier, mon application réagit-native ne passe pas au deuxième champ TextInput. Comment puis-je y arriver?

Merci!

113
andreaswienes

Vous pouvez faire ceci sans utiliser refs. Cette approche est préférable, car les références peuvent conduire à code fragile. Les React docs conseillent de trouver d’autres solutions dans la mesure du possible: 

Si vous n'avez pas programmé plusieurs applications avec React, votre premier L’inclination sera généralement d’essayer d’utiliser des références pour «faire avancer les choses arrive» dans votre application. Si tel est le cas, prenez un moment et réfléchissez davantage critique sur l'endroit où l'État devrait appartenir à la composante hiérarchie. Souvent, il devient évident que le lieu approprié pour "posséder" cela l'état est à un niveau supérieur dans la hiérarchie. Placer l'état là élimine souvent le désir d'utiliser des arbitres pour «faire avancer les choses» - Au lieu de cela, le flux de données permettra généralement d'atteindre votre objectif.

À la place, nous utiliserons une variable d'état pour focaliser le deuxième champ de saisie.

  1. Ajoutez une variable d'état que nous allons transmettre à la variable DescriptionInput:

    initialState() {
      return {
        focusDescriptionInput: false,
      };
    }
    
  2. Définissez une méthode de gestionnaire qui définira cette variable d'état sur true:

    handleTitleInputSubmit() {
      this.setState(focusDescriptionInput: true);
    }
    
  3. Lors de la soumission/en appuyant sur entrée/prochaine sur la TitleInput, nous appellerons handleTitleInputSubmit. Ceci définira focusDescriptionInput sur true.

    <TextInput 
       style = {styles.titleInput}
       returnKeyType = {"next"}
       autoFocus = {true}
       placeholder = "Title" 
       onSubmitEditing={this.handleTitleInputSubmit}
    />
    
  4. DescriptionInput 's focus prop est défini sur notre variable d'état focusDescriptionInput. Ainsi, lorsque focusDescriptionInput change (à l'étape 3), DescriptionInput sera restitué avec focus={true}.

    <TextInput
       style = {styles.descriptionInput}          
       multiline = {true}
       maxLength = {200}
       placeholder = "Description" 
       focus={this.state.focusDescriptionInput}
    />
    

C’est un bon moyen d’éviter d’utiliser des références, car les références peuvent conduire à un code plus fragile :)

EDIT: h/t à @LaneRettig pour indiquer que vous devez encapsuler React Native TextInput avec quelques accessoires et méthodes ajoutés pour qu’il réponde à focus:

    // Props:
    static propTypes = { 
        focus: PropTypes.bool,
    } 

    static defaultProps = { 
        focus: false,
    } 

    // Methods:
    focus() {
        this._component.focus(); 
    } 

    componentWillReceiveProps(nextProps) {
        const {focus} = nextProps; 

        focus && this.focus(); 
    }
44
Stedman Blake

Depuis React Native 0.36, l'appel de focus() (comme suggéré dans plusieurs autres réponses) sur un noeud de saisie de texte n'est plus pris en charge. Au lieu de cela, vous pouvez utiliser le module TextInputState de React Native. J'ai créé le module d'aide suivant pour faciliter cela:

// TextInputManager
//
// Provides helper functions for managing the focus state of text
// inputs. This is a hack! You are supposed to be able to call
// "focus()" directly on TextInput nodes, but that doesn't seem
// to be working as of ReactNative 0.36
//
import { findNodeHandle } from 'react-native'
import TextInputState from 'react-native/lib/TextInputState'


export function focusTextInput(node) {
  try {
    TextInputState.focusTextInput(findNodeHandle(node))
  } catch(e) {
    console.log("Couldn't focus text input: ", e.message)
  }
}

Vous pouvez alors appeler la fonction focusTextInput sur n'importe quel "ref" d'une TextInput. Par exemple:

...
<TextInput onSubmit={() => focusTextInput(this.refs.inputB)} />
<TextInput ref="inputB" />
...
25
Mitch

J'ai créé une petite bibliothèque qui effectue cette opération. Aucune modification de code n'est nécessaire, sauf le remplacement de la vue d'habillage et l'importation de TextInput: 

import { Form, TextInput } from 'react-native-autofocus'

export default () => (
  <Form>
    <TextInput placeholder="test" />
    <TextInput placeholder="test 2" />
  </Form>
)

https://github.com/zackify/react-native-autofocus

Expliqué en détail ici: https://zach.codes/autofocus-inputs-in-react-native/

17
zackify

À l’aide de react-native 0.45.1, j’ai également rencontré des problèmes lors de la définition du mot de passe TextInput après avoir appuyé sur la touche Entrée du nom d’utilisateur TextInput. 

Après avoir essayé la plupart des solutions les mieux notées ici sur SO, j'ai trouvé sur github une solution qui répondait à mes besoins: https://github.com/shoutem/ui/issues/44#issuecomment- 290724642 ​​

Résumer:

import React, { Component } from 'react';
import { TextInput as RNTextInput } from 'react-native';

export default class TextInput extends Component {
    render() {
        const { props } = this;

        return (
            <RNTextInput
                {...props}
                ref={(input) => props.inputRef && props.inputRef(input)}
            />
        );
    }
}

Et puis je l'utilise comme ça:

import React, {Component} from 'react';
import {
    View,
} from 'react-native';
import TextInput from "../../components/TextInput";

class Login extends Component {
    constructor(props) {
        super(props);
        this.passTextInput = null
    }

    render() {
        return (
            <View style={{flex:1}}>
                <TextInput
                    style={{flex:1}}
                    placeholder="Username"
                    onSubmitEditing={(event) => {
                        this.passTextInput.focus()
                    }}
                />

                <TextInput
                    style={{flex:1}}
                    placeholder="Password"
                    inputRef={(input) => {
                        this.passTextInput = input
                    }}
                />
            </View>
        )
    }
}
11
kuhr

Si vous utilisez tcomb-form-native comme je le suis, vous pouvez le faire aussi. Voici l’astuce: au lieu de définir directement les accessoires de TextInput, vous le faites via options. Vous pouvez vous référer aux champs du formulaire comme suit:

this.refs.form.getComponent('password').refs.input.focus()

Donc, le produit final ressemble à ceci:

var t = require('tcomb-form-native');
var Form = t.form.Form;

var MyForm = t.struct({
  field1:     t.String,
  field2:     t.String,
});

var MyComponent = React.createClass({

  _getFormOptions () {
    return {
      fields: {
        field1: {
          returnKeyType: 'next',
          onSubmitEditing: () => {this.refs.form.getComponent('field2').refs.input.focus()},
        },
      },
    };
  },

  render () {

    var formOptions = this._getFormOptions();

    return (
      <View style={styles.container}>
        <Form ref="form" type={MyForm} options={formOptions}/>
      </View>
    );
  },
});

(Nous remercions remcoanker d'avoir publié l'idée ici: https://github.com/gcanti/tcomb-form-native/issues/96 )

7
Lane Rettig

Pour moi sur RN 0.50.3, c'est possible de cette façon:

<TextInput 
  autoFocus={true} 
  onSubmitEditing={() => {this.PasswordInputRef._root.focus()}} 
/>

<TextInput ref={input => {this.PasswordInputRef = input}} />

Vous devez voir ceci.PasswordInputRef. _root . Focus ()

7
Wishmaster

C'est comme ça que je l'ai réalisé. Et l'exemple ci-dessous a utilisé l'API React.createRef () introduite dans React 16.3.

class Test extends React.Component {
  constructor(props) {
    super(props);
    this.secondTextInputRef = React.createRef();
  }

  render() {
    return(
        <View>
            <TextInput
                placeholder = "FirstTextInput"
                returnKeyType="next"
                onSubmitEditing={() => { this.secondTextInputRef.current.focus(); }}
            />
            <TextInput
                ref={this.secondTextInputRef}
                placeholder = "secondTextInput"
            />
        </View>
    );
  }
}

Je crois que ceci vous aidera.

4
Janaka Pushpakumara

Utilisation des références de rappel au lieu des références legacy string: 

<TextInput
    style = {styles.titleInput}
    returnKeyType = {"next"}
    autoFocus = {true}
    placeholder = "Title"
    onSubmitEditing={() => {this.nextInput.focus()}}
/>
<TextInput
    style = {styles.descriptionInput}  
    multiline = {true}
    maxLength = {200}
    placeholder = "Description"
    ref={nextInput => this.nextInput = nextInput}
/>
3
fagerbua

Essayez cette solution avec les problèmes GitHub de React Native.

https://github.com/facebook/react-native/pull/2149#issuecomment-12926252565

Vous devez utiliser le prop prop pour le composant TextInput.
Ensuite, vous devez créer une fonction qui est appelée sur le prop onSubmitEditing qui déplace le focus sur la deuxième réf. TextInput. 

var InputScreen = React.createClass({
    _focusNextField(nextField) {
        this.refs[nextField].focus()
    },

    render: function() {
        return (
            <View style={styles.container}>
                <TextInput
                    ref='1'
                    style={styles.input}
                    placeholder='Normal'
                    returnKeyType='next'
                    blurOnSubmit={false}
                    onSubmitEditing={() => this._focusNextField('2')}
                />
                <TextInput
                    ref='2'
                    style={styles.input}
                    keyboardType='email-address'
                    placeholder='Email Address'
                    returnKeyType='next'
                    blurOnSubmit={false}
                    onSubmitEditing={() => this._focusNextField('3')}
                />
                <TextInput
                    ref='3'
                    style={styles.input}
                    keyboardType='url'
                    placeholder='URL'
                    returnKeyType='next'
                    blurOnSubmit={false}
                    onSubmitEditing={() => this._focusNextField('4')}
                />
                <TextInput
                    ref='4'
                    style={styles.input}
                    keyboardType='numeric'
                    placeholder='Numeric'
                    blurOnSubmit={false}
                    onSubmitEditing={() => this._focusNextField('5')}
                />
                <TextInput
                    ref='5'
                    style={styles.input}
                    keyboardType='numbers-and-punctuation'
                    placeholder='Numbers & Punctuation'
                    returnKeyType='done'
                />
            </View>
        );
    }
});
3
APAquino

Pour que la solution acceptée fonctionne si votre TextInput se trouve dans un autre composant, vous devez "extraire" la référence de ref au conteneur parent.

// MyComponent
render() {
    <View>
        <TextInput ref={(r) => this.props.onRef(r)} { ...this.props }/>
    </View>
}

// MyView
render() {
    <MyComponent onSubmitEditing={(evt) => this.myField2.focus()}/>
    <MyComponent onRef={(r) => this.myField2 = r}/>
}
2
Eldelshell

Mon scénario est <CustomBoladonesTextInput /> encapsulant un RN <TextInput /> .

J'ai résolu ce problème comme suit:

Ma forme ressemble à:

  <CustomBoladonesTextInput 
      onSubmitEditing={() => this.customInput2.refs.innerTextInput2.focus()}
      returnKeyType="next"
      ... />

  <CustomBoladonesTextInput 
       ref={ref => this.customInput2 = ref}
       refInner="innerTextInput2"
       ... />

Sur la définition du composant CustomBoladonesTextInput, je passe le refField au prop interne comme ceci:

   export default class CustomBoladonesTextInput extends React.Component {
      render() {        
         return (< TextInput ref={this.props.refInner} ... />);     
      } 
   }

Et voila. Tout retourne à nouveau. J'espère que cela t'aides

2
Rodrigo Tessarollo
<TextInput placeholder="Nombre"
    ref="1"
    editable={true}
    returnKeyType="next"
    underlineColorAndroid={'#4DB6AC'}
    blurOnSubmit={false}
    value={this.state.First_Name}
    onChangeText={First_Name => this.setState({ First_Name })}
    onSubmitEditing={() => this.focusNextField('2')}
    placeholderTextColor="#797a7a" style={{ marginBottom: 10, color: '#808080', fontSize: 15, width: '100%', }} />

<TextInput placeholder="Apellido"
    ref="2"
    editable={true}
    returnKeyType="next"
    underlineColorAndroid={'#4DB6AC'}
    blurOnSubmit={false}
    value={this.state.Last_Name}
    onChangeText={Last_Name => this.setState({ Last_Name })}
    onSubmitEditing={() => this.focusNextField('3')}
    placeholderTextColor="#797a7a" style={{ marginBottom: 10, color: '#808080', fontSize: 15, width: '100%', }} />

et ajouter une méthode

focusNextField(nextField) {
    this.refs[nextField].focus();
}
2
Saloni Parikh

Il existe un moyen de capturer tabs dans une TextInput. C'est hacky, mais mieux que rien .

Définissez un gestionnaire onChangeText qui compare la nouvelle valeur d'entrée à l'ancienne, en recherchant un \t. S'il en trouve un, avancez le champ comme indiqué par @boredgames

En supposant que la variable username contient la valeur du nom d'utilisateur et que setUsername distribue une action pour la modifier dans le magasin (état du composant, magasin redux, etc.), procédez comme suit:

function tabGuard (newValue, oldValue, callback, nextCallback) {
  if (newValue.indexOf('\t') >= 0 && oldValue.indexOf('\t') === -1) {
    callback(oldValue)
    nextCallback()
  } else {
    callback(newValue)
  }
}

class LoginScene {
  focusNextField = (nextField) => {
    this.refs[nextField].focus()
  }

  focusOnPassword = () => {
    this.focusNextField('password')
  }

  handleUsernameChange = (newValue) => {
    const { username } = this.props            // or from wherever
    const { setUsername } = this.props.actions // or from wherever

    tabGuard(newValue, username, setUsername, this.focusOnPassword)
  }

  render () {
    const { username } = this.props

    return (
      <TextInput ref='username'
                 placeholder='Username'
                 autoCapitalize='none'
                 autoCorrect={false}
                 autoFocus
                 keyboardType='email-address'
                 onChangeText={handleUsernameChange}
                 blurOnSubmit={false}
                 onSubmitEditing={focusOnPassword}
                 value={username} />
    )
  }
}
1
marius

dans votre composant:

constructor(props) {
        super(props);
        this.focusNextField = this
            .focusNextField
            .bind(this);
        // to store our input refs
        this.inputs = {};
    }
    focusNextField(id) {
        console.log("focus next input: " + id);
        this
            .inputs[id]
            ._root
            .focus();
    }

Note: J'ai utilisé ._root parce que c'est une référence à TextInput dans NativeBase'Library 'Input

et dans vos entrées de texte comme ça

<TextInput
         onSubmitEditing={() => {
                          this.focusNextField('two');
                          }}
         returnKeyType="next"
         blurOnSubmit={false}/>


<TextInput      
         ref={input => {
              this.inputs['two'] = input;
                        }}/>
1
amorenew

Si vous utilisez NativeBase en tant que composants d'interface utilisateur, vous pouvez utiliser cet exemple


         <Item floatingLabel>
              <Label>Title</Label>
              <Input
              returnKeyType = {"next"}
              autoFocus = {true}
              onSubmitEditing={(event) => {
                  this._inputDesc._root.focus(); 
              }}
              />
          </Item>
          <Item floatingLabel>
              <Label>Description</Label>
              <Input
              getRef={(c) => this._inputDesc = c}
              multiline={true} style={{height: 100}} />
              onSubmitEditing={(event) => { this._inputLink._root.focus(); }}
          </Item>```
0

Voici une solution de réactif pour un composant d’entrée doté d’une propriété: focus.

Le champ sera concentré tant que cet accessoire est défini sur true et n'aura pas le focus tant que cela est faux.

Malheureusement, ce composant doit avoir un: ref défini, je n'ai pas pu trouver un autre moyen d'appeler .focus () dessus. Je suis content des suggestions.

(defn focusable-input [init-attrs]
  (r/create-class
    {:display-name "focusable-input"
     :component-will-receive-props
       (fn [this new-argv]
         (let [ref-c (aget this "refs" (:ref init-attrs))
               focus (:focus (ru/extract-props new-argv))
               is-focused (.isFocused ref-c)]
           (if focus
             (when-not is-focused (.focus ref-c))
             (when is-focused (.blur ref-c)))))
     :reagent-render
       (fn [attrs]
         (let [init-focus (:focus init-attrs)
               auto-focus (or (:auto-focus attrs) init-focus)
               attrs (assoc attrs :auto-focus auto-focus)]
           [input attrs]))}))

https://Gist.github.com/Knotschi/6f97efe89681ac149113ddec4c396cc5

0
Knotschi