React Native: как выбрать следующий TextInput после нажатия кнопки клавиатуры "далее"?

Я определил два поля TextInput следующим образом:

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

но после нажатия кнопки "Далее" на моей клавиатуре мое приложение react-native не переходит ко второму полю TextInput. Как я могу этого достичь?

спасибо!

15 ответов


второй TextInput фокус, когда предыдущий TextInput ' s onSubmitEditing срабатывает.

попробуй такое

  1. добавление ссылки на второй TextInput
    ref={(input) => { this.secondTextInput = input; }}

  2. привязать функцию фокусировки к первый TextInputonSubmitEditing событие.
    onSubmitEditing={() => { this.secondTextInput.focus(); }}

  3. Не забудьте установить blurOnSubmit в false, чтобы предотвратить клавиатуру колеблющийся.
    blurOnSubmit={false}

когда все сделано, это должно выглядеть так.

<TextInput
    placeholder = "FirstTextInput"
    returnKeyType = { "next" }
    onSubmitEditing={() => { this.secondTextInput.focus(); }}
    blurOnSubmit={false}
/>

<TextInput
    ref={(input) => { this.secondTextInput = input; }}
    placeholder = "secondTextInput"
/>

вы можете сделать это без использования refs. Этот подход является предпочтительным, так как refs может привести к хрупкий код. The React docs советую найти другие решения, где это возможно:

если вы не запрограммировали несколько приложений с React, ваш первый склонность, как правило, будет пытаться использовать refs для " создания вещей произошло" в вашем приложении. Если это так, найдите момент и подумайте больше критически о том, где государство должно быть в компоненте иерархия. Часто становится ясно, что правильное место ,чтобы "владеть", что состояние находится на более высоком уровне иерархии. Размещение государства там часто исключает любое желание использовать refs для того чтобы "сделать вещи случиться" – вместо этого поток данных обычно достигает вашей цели.

вместо этого мы будем использовать переменную состояния для фокусировки второго поля ввода.

  1. добавьте переменную состояния, которую мы передадим в качестве опоры DescriptionInput:

    initialState() {
      return {
        focusDescriptionInput: false,
      };
    }
    
  2. определите метод обработчика, который установит для этой переменной состояния значение true:

    handleTitleInputSubmit() {
      this.setState(focusDescriptionInput: true);
    }
    
  3. после отправки / нажатия enter / next на TitleInput, мы будем называть handleTitleInputSubmit. Это будет set focusDescriptionInput значение true.

    <TextInput 
       style = {styles.titleInput}
       returnKeyType = {"next"}
       autoFocus = {true}
       placeholder = "Title" 
       onSubmitEditing={this.handleTitleInputSubmit}
    />
    
  4. DescriptionInput ' s focus prop установлен на наш focusDescriptionInput переменной состояния. Итак, когда focusDescriptionInput изменения (в шаге 3), DescriptionInput будет повторно визуализировать с focus={true}.

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

это хороший способ избежать использования refs, так как refs может привести к более хрупкому коду:)

EDIT: h / t to @LaneRettig для указания на то, что вам нужно будет обернуть React Native TextInput с некоторыми добавленными реквизитами и методами, чтобы заставить его ответить на focus:

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

    static defaultProps = { 
        focus: false,
    } 

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

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

        focus && this.focus(); 
    }

по состоянию на React Native 0.36, вызывая focus() (как предложено в нескольких других ответах) на узле ввода текста больше не поддерживается. Вместо этого, вы можете использовать TextInputState модуль от React Native. Я создал следующий вспомогательный модуль, чтобы сделать это проще:

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

вы можете, тогда, вызвать focusTextInput функция на любом "ref"TextInput. Например:

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

Я создал небольшую библиотеку, которая делает это, никаких изменений кода не требуется, кроме замены вашего представления обертки и импорта 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

подробно объяснено здесь:https://zach.коды / автофокус-входы-в-реагировать-родной/


используя react-native 0.45.1 я также столкнулся с проблемами, пытаясь установить фокус на TextInput пароля после нажатия клавиши возврата на TextInput имени пользователя.

после того, как я попробовал большинство лучших решений здесь, поэтому я нашел решение на github, которое удовлетворило мои потребности: https://github.com/shoutem/ui/issues/44#issuecomment-290724642

в итоге:

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

и затем я использую его так:

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

если вы используете tcomb-form-native как я, Вы тоже можете это сделать. Вот трюк: вместо реквизита TextInput напрямую, Вы делаете это через options. Вы можете ссылаться на поля формы как:

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

так что конечный продукт выглядит примерно так:

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

(кредит remcoanker для размещения идеи здесь:https://github.com/gcanti/tcomb-form-native/issues/96)


для меня на RN 0.50.3 это возможно таким образом:

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

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

вы должны это увидеть.PasswordInputRef.строка _root.focus ()


попробуйте это решение на проблемах GitHub React Native.

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

вам нужно использовать ref prop для компонента TextInput.
Затем вам нужно создать функцию, которая вызывается onsubmitediting prop, которая перемещает фокус на второй TextInput ref.

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

использование ссылок обратного вызова вместо наследие строку ссылки:

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

для принятого решения работать, если ваш TextInput находится внутри другого компонента, вам нужно будет "поп" ссылку из ref к родительскому контейнеру.

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

вот как я этого достиг. И пример ниже использовал React.API createRef () представлен в 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>
    );
  }
}

Я думаю, это поможет вам.


есть способ захватить вкладки на TextInput. Это hacky, но лучше, чем ничего.

определение onChangeText обработчик, который сравнивает новое входное значение со старым, проверка \t. Если он найден, продвиньте поле, как показано @boredgames

предполагая, что переменная username содержит значение для имени пользователя и setUsername отправляет действие, чтобы изменить его в магазине (состояние компонента, redux store и т. д.), Что-то сделать вот так:

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

в компоненте:

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

Примечание: я использовал ._root потому что это ссылка на TextInput в 'Library NativeBase'

и в ваших текстовых вводах, как это

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


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

мой сценарий обертывание RN .

Я решил эту проблему следующим образом:

моя форма выглядит так:

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

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

в определении компонента CustomBoladonesTextInput я передаю поле refField во внутреннюю опору ref следующим образом:

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

и вуаля. Все снова работает. Надеюсь, это поможет


здесь решение реагента для входного компонента, имеющего свойство: focus.

поле будет сфокусировано до тех пор, пока эта опора установлена в true и не будет иметь фокуса, пока это false.

к сожалению, этот компонент должен иметь: ref определен, я не мог найти другой способ вызова .focus() on it. Я рад предложениям.

(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