React Native требует двух нажатий для изменения фокуса ввода в режиме прокрутки

Представьте себе простой ScrollView с несколькими TextInputs, как

  <ScrollView style={styles.container}>
    <TextInput style={styles.input} />
    <TextInput style={styles.input} />
  </ScrollView>

Когда я ввожу первый ввод, клавиатура открывается, и я могу печатать текст. Когда я хочу переключиться на второй ввод, мне нужно дважды нажать - первый тип закрывает клавиатуру, и только второе нажатие открывает клавиатуру для второго ввода.

Одним из решений является использование keyboardShouldPersistTaps={true} - переключение работает нормально, однако тогда клавиатура совсем не закрывается, и клавиатура может закрывать некоторые из последующих входов (или кнопок). Я также могу использовать keyboardDismissMode Однако это просто закрыть клавиатуру при перетаскивании.

Мой вопрос заключается в том, как объединить эти два поведения - в ИМХО для лучшего пользовательского опыта - когда я щелкаю по другому входу, фокус меняется немедленно, не открывая клавиатуру, и когда я нажимаю где-то еще, клавиатура закрывается?

Я использую RN0.22, и образец приложения доступен по адресу https://rnplay.org/apps/kagpGw

ОБНОВЛЕНИЕ - Эта проблема могла быть решена в RN 0,40 - см. https://github.com/facebook/react-native/commit/552c60192172f6ec503181c060c08bbc5cbcc5a4

7 ответов

Я решил свою проблему с этим кодом

<ScrollView
   keyboardDismissMode='on-drag'
   keyboardShouldPersistTaps={true}
>

В конце я нашел решение, которое не является оптимальным (с точки зрения кодирования), но работает с точки зрения пользователя. Я сделал небольшой компонент, который можно использовать вместо ScrollView:

export class InputScrollView extends React.Component {

  constructor(props, ctx) {
      super(props, ctx);
      this.handleCapture = this.handleCapture.bind(this);
  }

  render() {
    return (
      <ScrollView keyboardShouldPersistTaps={true} keyboardDismissMode='on-drag'>
        <View onStartShouldSetResponderCapture={this.handleCapture}>
          {this.props.children}
        </View>
      </ScrollView>
    );
  }

  handleCapture(e) {
    const focusField = TextInputState.currentlyFocusedField();
    const target = e.nativeEvent.target;
    if (focusField != null && target != focusField){
      const inputs = this.props.inputs;
      if (inputs && inputs.indexOf(target) === -1) {
        dismissKeyboard();
      }
    }
  }
}

InputScrollView.propTypes = {
  inputs : React.PropTypes.array,
}

Единственным недостатком является то, что мне нужно собирать дескрипторы узла (как возвращено React.findNodeHandle) всех текстовых вводов "вручную" и передать его компоненту в виде массива.

Этот SO-ответ не совсем то, что вы просите, но автоматически выдвинет окно из-за клавиатуры, когда TextInput имеет фокус; Решение вашей клавиатуры может покрыть некоторые из последующих проблем ввода (или кнопок).

Просматривая https://facebook.github.io/react-native/docs/scrollview.html, я обнаружил метод, который может сделать это:

  1. После ввода в TextInput1, если вы нажмете Input2, клавиатура сохранится.
  2. Если щелкнуть где-нибудь пусто, клавиатура автоматически исчезнет.

Код выглядит следующим образом:

<ScrollView
  keyboardDismissMode="none"
  keyboardShouldPersistTaps="handled"
>
  {content}
</ScrollView>

В новых версиях реакции-натива вы можете сохранить событие нажатия на прокрутку, как показано ниже:

<ScrollView
      keyboardShouldPersistTaps='always'
      keyboardDismissMode={'interactive'}></ScrollView>

Главный трюк keyboardShouldPersistTaps='always'

Я обновил приведенный выше ответ, потому что клавиатура ShouldPersistTaps={true} устарела и теперь используется клавиатура ShouldPersistTaps='всегда'

export class InputScrollView extends React.Component {

  constructor(props, ctx) {
      super(props, ctx);
      this.handleCapture = this.handleCapture.bind(this);
  }

  render() {
    return (
      <ScrollView keyboardShouldPersistTaps='always' keyboardDismissMode='on-drag'>
        <View onStartShouldSetResponderCapture={this.handleCapture}>
          {this.props.children}
        </View>
      </ScrollView>
    );
  }

  handleCapture(e) {
    const focusField = TextInputState.currentlyFocusedField();
    const target = e.nativeEvent.target;
    if (focusField != null && target != focusField){
      const inputs = this.props.inputs;
      if (inputs && inputs.indexOf(target) === -1) {
        dismissKeyboard();
      }
    }
  }
}

InputScrollView.propTypes = {
  inputs : React.PropTypes.array,
}

Если keyboardShouldPersistTaps="always"не работает для вас, проверьте свою кнопку и действие, которое она выполняет.
Наличие чего-то вроде приведенного ниже кода может привести к проблеме с двумя касаниями независимо от keyboardShouldPersistTaps ценить:

      const submit = () => {
  const _valid = some_validator();
  setValid(_valid);

  if (valid) {
    // expected action
    // may not fire on first press due to the state update call above
    api.someAction();
  } else {
    // some error thing
  }
};

Вместо этого вы можете использовать useEffect чтобы разделить вызов состояния кнопки и результирующее действие, чтобы событие щелчка имело единственную цель

      useEffect(()=>{
  if(valid) {
    // expected action
    api.someAction();
  }
}, [valid])
Другие вопросы по тегам