React原生听写在iOS上突然切断了单词

时间:2018-05-08 20:49:54

标签: javascript ios reactjs react-native

在iOS中的TextInput上使用听写时,听写将在单词之间突然结束。这在以前的React Native 53上不是问题。移至版本54+会导致此行为。

这是产生错误的代码示例:

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

export default class App extends Component {
  state = { value: '' }
  onChangeText = value => console.log(value) || this.setState({ value })
  render() {
    return (
        <TextInput
          onChangeText={this.onChangeText}
          value={this.state.value}
          style={{ borderWidth: 2, borderColor: 'black', width: 200, height: 48 }}
        />
    );
  }
}

似乎来自onChangeText方法,因为当我将函数onChangeText放入onBlur方法时,它运行良好。 但是使用onBlur无法使用onEditingonSubmitEditing,因为它会在setStateonEditing方法之后触发onSubmitEditing

有人找到了解决方法吗?

在github react本地项目here上打开了一个问题。

1 个答案:

答案 0 :(得分:1)

所以这是一个反应本地的短缺。

听写失败的原因是因为组件在您指示时重新呈现它。每当它重新呈现时,Siri小组就会最小化,因此听写会突然结束。

为了解决这个问题,我创建了一个TextInput包装器组件,它依赖于shouldComponentUpdate来阻止我的TextInput包装器在值发生变化时重新呈现。

现在,我们不是立即重新渲染,而是在经过一段特定时间的去抖后才setState。 (任何500-1500的东西都应该有用)

尝试一下,让我知道它是否适合你。

import React from 'react';
import debounce from 'lodash.debounce';
import PropTypes from 'prop-types';
import { TextInput } from 'react-native';

const CHANGE_TEXT_DELAY_UNTIL_DISPATCH = 700;

// enforces dication safety so that siri can hear more than 1 words
class DictationSafeTextInput extends React.Component {
  //
  constructor(props) {
    super(props);
    this.onValueChangeDelayed =
      debounce(this.onValueChangeDelayed.bind(this), CHANGE_TEXT_DELAY_UNTIL_DISPATCH);
    this.state = {
      value: props.value,
    };
  }


  componentWillReceiveProps(nextProps) {
    if (this.props.value !== nextProps.value) {
      this.onValueChangeDelayed(nextProps.value);
    }
  }

  shouldComponentUpdate(nextProps, nextState) {
    const pastProps = this.props;
    const shoudlUpdate = (
      // pastProps.value !== nextProps.value
      // ||
      nextState.value !== this.state.value
      ||
      pastProps.style !== nextProps.style
      ||
      pastProps.editable !== nextProps.editable
      ||
      pastProps.onContentSizeChange !== nextProps.onContentSizeChange
      ||
      pastProps.onSubmitEditing !== nextProps.onSubmitEditing
      ||
      pastProps.onChangeText !== nextProps.onChangeText
      ||
      pastProps.onFocus !== nextProps.onFocus
      ||
      pastProps.onBlur !== nextProps.onBlur
      ||
      pastProps.innerTextInputRef !== nextProps.innerTextInputRef
      ||
      pastProps.blurOnSubmit !== nextProps.blurOnSubmit
      ||
      pastProps.autoFocus !== nextProps.autoFocus
      ||
      pastProps.pointerEvents !== nextProps.pointerEvents
      ||
      pastProps.maxLength !== nextProps.maxLength
      ||
      pastProps.returnKeyType !== nextProps.returnKeyType
      ||
      pastProps.placeholderTextColor !== nextProps.placeholderTextColor
      ||
      pastProps.placeholder !== nextProps.placeholder
      ||
      pastProps.underlineColorAndroid !== nextProps.underlineColorAndroid
      ||
      pastProps.autoCorrect !== nextProps.autoCorrect
      ||
      pastProps.multiline !== nextProps.multiline
      ||
      pastProps.autoCapitalize !== nextProps.autoCapitalize
      ||
      pastProps.keyboardType !== nextProps.keyboardType
      ||
      pastProps.numberOfLines !== nextProps.numberOfLines
      ||
      pastProps.defaultValue !== nextProps.defaultValue
      ||
      pastProps.dictationSafety !== nextProps.dictationSafety
    );
    return shoudlUpdate;
  }


  componentWillUnmount() {
    this.onValueChangeDelayed.cancel();
    if (this.onSubmitEditingTimeout != null) {
      clearTimeout(this.onSubmitEditingTimeout);
      this.onSubmitEditingTimeout = null;
    }
  }

  onValueChangeDelayed(newValue) {
    if (newValue !== this.state.value) {
      this.setState({
        value: newValue,
      });
    }
  }


  render() {
    const {
      dictationSafety,
      onChangeText,
      onBlur,
      innerTextInputRef,
      onSubmitEditing,
    } = this.props;
    if (dictationSafety && onChangeText) {
      return (
        <TextInput
          ref={(r) => {
            if (innerTextInputRef != null) {
              innerTextInputRef(r);
            }
          }}
          {...this.props}
          value={this.state.value}
          onChangeText={(newValue) => {
            if (this.props.onChangeText) {
              this.props.onChangeText(newValue);
            }
          }}
          onBlur={() => {
            this.onValueChangeDelayed.flush();
            if (onBlur) {
              onBlur();
            }
          }}
          onSubmitEditing={() => {
            this.onValueChangeDelayed.flush();
            if (onSubmitEditing) {
              this.onSubmitEditingTimeout = setTimeout(() => {
                onSubmitEditing();
              }, CHANGE_TEXT_DELAY_UNTIL_DISPATCH);
            }
          }}
        />
      );
    }
    return (
      <TextInput
        {...this.props}
      />
    );
  }
}

DictationSafeTextInput.defaultProps = {
  dictationSafety: true,
};
DictationSafeTextInput.propTypes = {
  innerTextInputRef: PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.object,
  ]),
  onChangeText: PropTypes.func,
  onBlur: PropTypes.func,
  onSubmitEditing: PropTypes.func,
  dictationSafety: PropTypes.bool,
  pointerEvents: PropTypes.oneOf([
    'box-none',
    'none',
    'box-only',
    'auto',
  ]),
  autoCorrect: PropTypes.bool,
  style: PropTypes.oneOfType([
    PropTypes.object,
    PropTypes.array,
    PropTypes.number,
  ]),
  onContentSizeChange: PropTypes.func,
  editable: PropTypes.bool,
  // eslint-disable-next-line
  underlineColorAndroid: PropTypes.any,
  value: PropTypes.string,
  multiline: PropTypes.bool,
  placeholder: PropTypes.string,
  // eslint-disable-next-line
  placeholderTextColor: PropTypes.any,
  returnKeyType: PropTypes.oneOf([
    // Cross-platform
    'done',
    'go',
    'next',
    'search',
    'send',
    // Android-only
    'none',
    'previous',
    // iOS-only
    'default',
    'emergency-call',
    'google',
    'join',
    'route',
    'yahoo',
  ]),
  autoFocus: PropTypes.bool,
  blurOnSubmit: PropTypes.bool,
  autoCapitalize: PropTypes.oneOf([
    'none',
    'sentences',
    'words',
    'characters',
  ]),
  keyboardType: PropTypes.oneOf([
    'default',
    'email-address',
    'numeric',
    'phone-pad',
    'number-pad',
    'ascii-capable',
    'numbers-and-punctuation',
    'url',
    'name-phone-pad',
    'decimal-pad',
    'twitter',
    'web-search',
    'visible-password',
  ]),
  onFocus: PropTypes.func,
  numberOfLines: PropTypes.number,
  defaultValue: PropTypes.string,
  maxLength: PropTypes.number,
};

export default DictationSafeTextInput;