When using dictation on a TextInput in iOS, the dictation will end abruptly between words. This was not an issue previously on React Native 53. Moving to version 54+ causes this behavior.
Environment:
OS: macOS High Sierra 10.13.4
Node: 9.7.1
Yarn: 1.5.1
npm: 5.6.0
Watchman: Not Found
Xcode: Xcode 9.3 Build version 9E145
Android Studio: Not Found
Packages: (wanted => installed)
react: 16.3.0-alpha.1 => 16.3.0-alpha.1
react-native: https://github.com/expo/react-native/archive/sdk-26.0.0.tar.gz => 0.54.2
Try using iOS dictation in the TextInput. Be sure to use a long sentence...
import React from 'react';
import { TextInput } from 'react-native';
export default class App extends React.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 }}
/>
);
}
}
I expect the TextInput to capture the entire dictation.
The dictation will end abruptly between words.
If I place a breakpoint on setAttributedText (line 101) on this file:
react-native/Libraries/Text/TextInput/RCTBaseTextInputView.m
and wait a second or two, then resume the app, the dictated sentence does not get cut off.
Hmm...after additional debugging, seems like the text inserted by the dictation works “too fast” for setState. If i move the onChangeText to onBlur, it doesn’t get cut off.
I have this problem too. Is that a problem? Or are you ready to use the onBlur method to get the data that is being entered by Textinput? Version 0.49.2 is fine, but 0.55 will also appear.
@marcosrdz moving setState in onBlur will make useless all the other props such as onEndEditing or onSubmitEditing because they will fire before setState take the previous state
I have solved this issue using lodash.debounce.
import _ from 'lodash'
class SomeComponent extends React.Component {
constructor(props){
super(props);
this.state = {
text: ''
}
this.onChangeTextDelayed = _.debounce(this.onChangeText.bind(this), 1500)
}
onChangeText(text) {
this.setState({text})
}
render() {
return (
<TextInput
onBlur={()=>{
this.onChangeTextDelayed.flush()
}}
onChangeText={this.onChangeTextDelayed}
value={this.state.text}
/>
)
}
}
As the debounce needs to be invoked immediately after the user stops editing (voice or text), the .flush method of the debounced function needs to be called in the onBlur handler.
You might need to tweak the debounce timeout, but I've found anything between 1 and 2 sec to be useable for dictation.
The fix in react native is merely disguising the problem. The underlying problem is that when the setAttributedText method in RCTBaseTextInputView.m is called it does two main things:
1) Check that the attributed text that it receives is equal to the existing attributed text in the underlying backed up view ( backedTextInputView)
2) If not set the attributed text of the backed up view to the value passed into the method. This kills the dictation as it is effectively the equivalent of typing in the backed up text view.
self.backedTextInputView.attributedText = attributedText;
is the problem. It kills the dictation. It may have other effects as well.
In all cases I have seen the underlying text of the attributed string that is passed in the same as the text in the backedTextInputView, what was said to the dictation; however the attributes are different, which causes the isEqualToAttributedString: check to fail and thus the update happens, and the keyboard is killed.
(No doubt this is to impose whatever properties were set in the JS layer to the underlying text view)
The debounce above will stop this method being called for 1.5 seconds, which fixes it for that length of time only.
I also faced the same issue. You can use shouldComponentUpdate to not re-render that text input component while setting the state.
The js solution above does not work if you rely on onChangeText heavily and use something like redux.
Sometimes the onBlur function flushes the debounce method before the new value has been set in redux via onChangeText.
We found out that's the case the hard way (by having people type stuff, and even though it shows on screen, the submitted text was less than what they had typed...
We need to think of a better solution.
Fixed the issue by debouncing the value change and not the onChangeText callback.
Here's the fully operational component --> https://gist.github.com/SudoPlz/4c35ca33af44d8ac998d836fb4552627
Any other better solution with in iOS itself?
Our App is broken in production because of this issue. Any ETA when @react-native-community going to take it up. @SudoPlz
Any updates @shergin
@AshokICreate have you tried the potential fix I linked?
Same issue here, cut off after 1 minute of dictation.
i down graded react-native to 0.53 to make it work.
FWIW I ran into the same frustrating issue with onChangeText. I managed to get a work-able solution using @arvidkahl's _.debounce approach.
It's not perfect and I'm looking for, and expecting edge cases to arise but it will at least provide a somewhat usable experience, especially for those users who must navigate apps with core iOS and Android accessibility features.
https://github.com/facebook/react-native/issues/18890#issuecomment-387345803
It looks like this may have been fixed in 0.57.x. Here is a detailed analysis of why this bug occurs.
We tried upgrading to 0.57.1 but ran into some issues and bailed out. The work around we found for now, is to type anything into the text box first, then dictation can be used normally without it exiting. Weird that the bug only exhibits itself when the text box is empty...
Would be nice to confirm that it for sure works in 0.57.x.
Hope this helps!
Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as "For Discussion" or "Good first issue" and I will leave it open. Thank you for your contributions.
Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please feel free to create a new issue with up-to-date information.
Most helpful comment
I have solved this issue using
lodash.debounce.As the debounce needs to be invoked immediately after the user stops editing (voice or text), the
.flushmethod of the debounced function needs to be called in theonBlurhandler.You might need to tweak the debounce timeout, but I've found anything between 1 and 2 sec to be useable for dictation.