简体   繁体   English

React Native Siri Dictation 被重新渲染缩短 - 功能组件

[英]React Native Siri Dictation cut short by rerenders - Functional Component

I have a react native prodject.我有一个反应原生项目。

I have an input that I use in several places in the app.我有一个输入,我在应用程序的几个地方使用。 I can type in it no problem, but I use siri dictation fromt the ios keyboard, the words are cut short by a rerender.我可以输入它没问题,但我使用 ios 键盘的 siri 听写,单词被重新渲染缩短了。

A similar questions has been asked before, React native dictation cuts words abruptly on iOS but the only answer was for class components.之前有人问过类似的问题, React native dictation 在 iOS 上突然断词,但唯一的答案是 class 组件。 Is there a way to fix this with functional components?有没有办法用功能组件解决这个问题?

I tried throwing a useMemo() around the textChangeHandler, and that does allow siri to work, by blocking all state updates.我尝试在 textChangeHandler 周围抛出一个 useMemo(),通过阻止所有 state 更新,这确实允许 siri 工作。 But this is no good because then I have no data.但这不好,因为那时我没有数据。

Here is my component:这是我的组件:

import React, { useReducer, useEffect, useRef } from 'react';
import {
  StyleSheet,
  View,
  Text,
  TextInput,
  TouchableOpacity,
} from 'react-native';
import mergeRefs from 'react-merge-refs';
import PropTypes from 'prop-types';

const INPUT_CHANGE = 'INPUT_CHANGE';
const INPUT_BLUR = 'INPUT_BLUR';

const formatDate = date => {
  const options = {
    month: 'numeric',
    day: 'numeric',
    year: '2-digit',
  };
  const formattedDate = new Date(date);
  const _formattedDate = formattedDate.toLocaleDateString('en-US', options);
  return _formattedDate;
};

const inputReducer = (state, action) => {
  switch (action.type) {
    case INPUT_CHANGE:
      return {
        ...state,
        value: action.value,
        isValid: action.isValid,
      };
    case INPUT_BLUR:
      return {
        ...state,
        touched: true,
      };

    default:
      return state;
  }
};

const Input = React.forwardRef((props, ref) => {
  const [inputState, dispatch] = useReducer(inputReducer, {
    value: props.initialValue ? props.initialValue : '',
    isValid: props.initiallyValid ? props.initiallyValid : true,
    touched: props.initialValue ? true : false,
  });

  const { onInputChange, id } = props;

  useEffect(() => {
    onInputChange(id, inputState.value, inputState.isValid);
  }, [inputState, onInputChange, id]);

  const textChangeHandler = text => {
    const emailRegex =
      /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
    let isValid = true;
    if (props.required && text.trim().length === 0) {
      isValid = false;
    }
    if (props.email && !emailRegex.test(text.toLowerCase())) {
      isValid = false;
    }
    if (props.min != null && +text < props.min) {
      isValid = false;
    }
    if (props.max != null && +text > props.max) {
      isValid = false;
    }
    if (props.minLength != null && text.length < props.minLength) {
      isValid = false;
    }

    dispatch({ type: INPUT_CHANGE, value: text, isValid: isValid });
  };

  const lostFocusHandler = () => {
    dispatch({ type: INPUT_BLUR });
  };

  const inputRef = useRef();
  const getFormValue = () => {
    const inputValue = props.initialValue
      ? props.initialValue
      : inputState.value;
    if (props.date) {
      return formatDate(inputValue).toString();
    }
    return inputValue;
  };

  return (
    <View style={{ ...props.style, ...styles.container }}>
      <TextInput
        ref={mergeRefs([inputRef, ref])}
        {...props}
        value={getFormValue()}
        onChangeText={textChangeHandler}
        onBlur={lostFocusHandler}
      />

      {!inputState.isValid && inputState.touched && (
        <TouchableOpacity onPress={() => inputRef.current.focus()}>
          <View style={{ ...props.style, ...styles.errorContainer }}>
            <Text
              testID="Auth.errorMessage"
              style={{ color: props.errorTextColor, ...styles.errorText }}
            >
              {props.errorText}
            </Text>
          </View>
        </TouchableOpacity>
      )}
    </View>
  );
});

const styles = StyleSheet.create({
  container: { flex: 1 },
  errorContainer: {
    marginVertical: 5,
  },
  errorText: {
    fontSize: 13,
  },
});

Input.displayName = 'Input'; //This is here only to make esLint happy

Input.propTypes = {
  date: PropTypes.bool,
  onInputChange: PropTypes.func,
  id: PropTypes.string.isRequired,
  label: PropTypes.string.isRequired,
  initialValue: PropTypes.any,
  initiallyValid: PropTypes.bool,
  required: PropTypes.bool,
  email: PropTypes.bool,
  min: PropTypes.number,
  max: PropTypes.number,
  minLength: PropTypes.number,
  style: PropTypes.object,
  errorText: PropTypes.string,
  errorTextColor: PropTypes.string,
};

export default Input;

Well, I guess I forgot to answer this before I figured out a solution.好吧,我想我在找到解决方案之前忘了回答这个问题。 It's a bit hacky, but I just wrapped the contents of getFormValue in a 5ms timeout, and that was enough to keep it from bugging out.这有点 hacky,但我只是将 getFormValue 的内容包装在 5ms 超时中,这足以防止它出错。

  const getFormValue = () => {
    setTimeout(() => {
      const inputValue = props.initialValue
        ? props.initialValue
        : inputState.value;
      if (props.date) {
        return formatDate(inputValue).toString();
      }
      return inputValue;
    }, 5);
  };

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM