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

2023-12-27

在 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 in onBlur方法效果很好。 但使用onBlur使得不可能使用onEditing nor onSubmitEditing因为它会火setState之后onEditing and onSubmitEditing方法。

有人找到了解决这个问题的方法吗?

在 github React Native 项目上打开了一个问题here https://github.com/facebook/react-native/issues/18890.


所以这是react-native的一个缺点。

听写失败的原因是当您听写时组件会自行重新渲染。每当它重新渲染时,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;
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

React 原生听写在 iOS 上突然切词 的相关文章

  • 如何监听 jQuery AJAX 请求?

    以下两种实现 ajaxRequest 1 2 的方法应该是等效的 话说回来 为什么验证回调已执行的单元测试 3 在 1 中成功而在 2 中失败 我应该如何重写测试 3 来监视 2 中的成功回调 如果我尝试stub jQuery ajax使用
  • 除了更改标题之外,如何在 Firefox 中强制另存为对话框?

    有没有办法在 ff 中强制打开 www example com example pdf 的另存为对话框 我无法更改标题 如果您可以将文件以 Base64 格式输出到客户端 则可以使用 data uri 进行下载 location href
  • 从未用 @flow 标记的导入文件中获取类型定义

    TL DR我怎么告诉flow从未声明的导入模块导入类型定义 flow 加长版 流接缝能够从不使用流语法的文件中派生类型 请参阅示例 示例文件 flow js if Math random lt 0 5 var y hello else va
  • 访问目标 c 中的类方法。使用 self 还是类名?

    我正在学习 iOS 编程 并且对以下有关关键字 self 的使用的代码感到困惑 据我了解 self就像Java的this 它指的是当前实例 当我想调用类方法时 通常的方式应该是这样 PlayingCard validSuits 但是侵入实例
  • 标签获取 href 值

    我有以下 html div class threeimages a img alt Australia src Images Services 20button tcm7 9688 gif a div class text h2 a hre
  • Jquery/Javascript 上传和下载文件,无需后端

    是否可以在没有后端服务器的情况下在 JavaScript 函数中下载和上传文件 我需要导出和导入由 JavaScript 函数生成的 XML 我想创建按钮 保存 xml 来保存文件 但我不知道是否可行 另一方面 我希望将 XML 文件直接上
  • React - 无法读取未定义的属性[重复]

    这个问题在这里已经有答案了 通常 当我单击子组件中的菜单项时 它会调用 this handlesort 这是一个本地函数 处理排序从我的父组件中获取 onReorder 属性 onReorder 调用名为 reOrder 的本地函数 它设置
  • MVC 在布局代码之前执行视图代码并破坏我的脚本顺序

    我正在尝试将所有 javascript 包含内容移至页面底部 我正在将 MVC 与 Razor 一起使用 我编写了一个辅助方法来注册脚本 它按注册顺序保留脚本 并排除重复的内容 Html RegisterScript scripts som
  • Meteor - 从客户端取消服务器方法

    我正在通过服务器方法执行数据库计数 用户可以选择他们希望如何执行计数 然后调用该方法 我的问题是 计数可能需要一些时间 并且用户可能会在方法运行时改变主意并请求不同的计数 有什么方法可以取消调用的方法并运行新的计数吗 我认为 this un
  • 通过 CDN 使用 Dojo 时如何加载自定义 AMD 模块?

    我正在使用 google 的 CDN 并尝试使用他们的加载程序加载我自己的 AMD 模块 我知道我做错了什么 但我被困住了 有任何想法吗
  • 如何使输入字段和提交按钮变灰

    我想变灰这两件事 http doorsplit heroku com 歌曲输入字段和提交按钮 直到用户输入艺术家 有没有一种简单的方法可以通过 JQuery 来做到这一点 艺术家输入字段的id是 request artist 你可以这样做
  • Firefox 书签探索未超过 Javascript 的第一级

    我已经编写了一些代码来探索我的 Firefox 书签 但我只获得了第一级书签 即我没有获得文件夹中的链接 e g 搜索引擎 雅虎网站 谷歌网站 在此示例中 我只能访问 Search engines 和 google com 不能访问 yah
  • firebase :: 无法读取 null 的属性“props”

    你好 我正在尝试将react router与firebase一起使用 但它给了我这个错误 无法读取 null 的属性 props 这正是代码 我正在其中使用我的反应路由器 向下代码位于作为登录面板组件的组件上 else if this em
  • Grails 在 javascript 内的 GSP 站点中使用 grails var

    我有一个在 GSP 文件中的 javascript 代码中使用 grails 变量值的问题 例如 我有一个会话值session getAttribute selectedValue 我想在 javascript 代码部分使用这个值 我现在的
  • iOS:如何知道 reloadData() 已完成其任务?

    我想滚动到给定索引 self boldRowPath 但是当我调试时scrollToRow之前执行reloadData 如何知道reloadData已完成 func getAllTimeEvent self arrAllTimeEvent
  • 如何在类似控制台的环境中运行 JavaScript?

    我正在尝试遵循这里的示例 http eloquentjavascript net chapter2 html http eloquentjavascript net chapter2 html and print blah 在浏览器中运行时
  • 条件在反应本机生产中失败,但在开发中有效

    我创建了一个反应本机应用程序 我需要通过它进行比较 如果属实 就会执行死刑 问题是 该条件适用于 React Native 开发模式 而不适用于 React Native 生产版本 我使用 firebase 作为数据库 也使用 redux
  • 将 MQTTNet 服务器与 MQTT.js 客户端结合使用

    我已经启动了一个 MQTT 服务器 就像this https github com chkr1011 MQTTnet tree master例子 该代码托管在 ASP Net Core 2 0 应用程序中 但我尝试过控制台应用程序 但没有成
  • 在 React.js 中编辑丰富的数据结构

    我正在尝试为数据结构创建一个简单的基于网格的编辑器 但我在使用 React js 时遇到了一些概念问题 他们的文档对此没有太大帮助 所以我希望这里有人可以提供帮助 首先 将状态从外部组件传输到内部组件的正确方法是什么 是否有可能将内部组件中
  • 像 TraceGL 一样分析 Objective C 中的代码路径?

    TraceGL 是一个非常简洁的项目 它允许 JS 程序员跟踪 Javascript 中的代码路径 它看起来像这样 我想为 Objective C 构建类似的东西 我知道运行时使跟踪方法调用变得相当容易 但是我如何跟踪控制流 例如 在上面的

随机推荐