react-native android: TextInput briefly ignores value prop - android

This is on android with react-native v0.55.4.
<TextInput
value="should always be this value"
/>
When I am typing inside this TextInput, the text will briefly update with whatever new text I enter, before going back to showing "should always be this value".
For example, if I type 'X', the text will briefly update to "should always be this valueX", before returning back to "should always be this value", creating a jitter inside the TextInput.
edit: the docs addressed this issue: https://facebook.github.io/react-native/docs/textinput#value

Based on just that short codesnippet, you need to set value to a state property.
constructor(props) {
super(props);
this.state = {
textInput: 'Should always be this value'
}
}
And your text input
<TextInput
value={this.state.textInput}
onChangeText={text => this.setState({ textInput: text }) }
/>
Here is some docs

Related

React-navigation touches sometimes don't work

I'm hoping this is a known problem as I can't provide much to go on to get help solving it.
I'm using react-navigation for my app with the following setup:
AppContainer
Splash Screen
Setup Screen (A stack navigator)
Main screen (A stack navigator)
When my app starts it goes to the splash screen which decides if this is the first time running or not and depending on this decision calls this.props.navigation.navigate() with either main screen or setup screen.
So far this all works and is fairly standard.
When my app is launched for the first time the user is sent to the setup screen where they navigate through a series of screens entering data and selecting a next button to proceed to the next screen. This is where the problem occur. My first screen simply has some text and a next button (which is a regular button component) which when clicked calls this.props.navigation.push('nextviewname', {data: data}) to move to the next view.
The next view contains a textinput as well as back and next buttons which is where I'm having problems. When I reach this screen after freshly installing a release version of my app onto my Android X phone none of the inputs work. I can't:
Click next of back
Click the back arrow in the top left that is part of the header
Click the text input (the cursor does briefly show up in the text input but the keyboard never appears)
Click the hardware back key
On very rare occasions some of the above does work (e.g. the text input will sometimes work) and sometimes I'll even make it to the next step of my setup but it's rare that I make it all the way through
Weirdly this all works when I'm debugging my app on my phone.
Update: I've just tested on an Android 9 emulator and I'm getting the same issue.
Update 2: This is getting weird, when the app is in a broken state I can still bring up the react native debug menu however when I click Toggle Inspector nothing happens (i.e. I don't get the inspector UI). It's looking like this is somehow breaking everything.
Has anyone seen/solved this issue before? At the moment it's effectively made my app useless.
Update 3: Some code to hopefully make things clearer:
const SetupUser = createStackNavigator(
{
SetupUser: WelcomeScreen,
SetupName: UserName,
SetupCurrentWeight: CurrentWeight,
SetupGoalWeight: GoalWeight,
SetupGoalDate: GoalDate,
Summary: Summary,
LogWeight: LogWeight,
},
{
defaultNavigationOptions: {
headerStyle: {
backgroundColor: '#001830',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
},
},
);
const MainApp = createStackNavigator(
{
LogWeight: LogWeight,
LogWeightSummary: LogWeightSummary,
},
{
defaultNavigationOptions: {
headerStyle: {
backgroundColor: '#001830',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
},
},
);
export default createAppContainer(
createSwitchNavigator(
{
MainApp: MainApp,
SplashScreen: SplashScreen,
SetupUser: SetupUser,
},
{
initialRouteName: 'SplashScreen',
},
),
);
In getting this code snippit together (I've removed the tab navigator as the error is still there even without it) I think I've managed to track down the source of the issue however I'm still not sure how to fix it. The first view loaded is the splash screen which looks like this:
export default class SplashScreen extends React.Component {
constructor(props) {
super(props);
GoBinder.verifyDatabase()
.then(GoBinder.getLastUser())
.then(user => {
this.props.navigation.navigate(
user.user == null ? 'SetupUser' : 'MainApp',
);
})
.catch(error => {
GoBinder.toast('Error while checking initial user state: ' + error);
});
}
render() {
return (
<View style={styles.container}>
<ActivityIndicator />
<StatusBar barStyle="default" />
</View>
);
}
}
In the above code, GoBinder.verifyDatabase() and GoBinder.getLastUser() are calls to native code which perform some database operation to get things setup and check to see if there are any existing users. I believe the issue is this.props.navigation.navigate is firing too quickly which is causing react-navigation to load the next screen but get messed up in the process.
I've tried following the suggestions in this post https://www.novemberfive.co/blog/react-performance-navigation-animations about moving blocking code into InteractionManager.runAfterInteractions under componentDidMount however this made no difference. However, it I manually trigger the move to the new screen using a button everything works correctly so its really looking like the act of programatically changing screens is messing things up.
Update 4:
Looks like I jumped the gun a bit, its still freezing up a fair bit, moving my answer into an update:
So I'm not sure if this is the best solution but I have found a workaround. I am now calling my database code in InteractionManager.runArfetInteraction() as suggested in https://www.novemberfive.co/blog/react-performance-navigation-animations. I am then calling setState with the result of the DB functions to store the result. Finally, I am using a callback on setState to trigger the actual navigation. My complete working code is:
componentDidMount() {
InteractionManager.runAfterInteractions(() => {
GoBinder.verifyDatabase()
.then(
GoBinder.getLastUser().then(user => {
this.setState(
{
user: user.user,
},
() => {
this.props.navigation.navigate(
this.state.user == null ? 'SetupUser' : 'MainApp',
);
},
);
}),
)
.catch(error => {
GoBinder.toast('Error while checking initial user state: ' + error);
});
});
}
Thanks for your help

I want to clear the text input by providing a clear button. While iOS has support for this, I did not find any solution for android

I am totally new to React Native. I have a textinput area, I want users to clear the text they have entered completely by clicking on a button. React native provides clearButtonMode, but that is only for iOS. Looking for a solution on android devices. Here is my textinput..
<View style={editProfileStyle.textinputView}>
<TextInput
underlineColorAndroid={"rgba(0,0,0,0)"}
style={editProfileStyle.textInput}
placeholder="Enter your Name"
value={this.state.name}
onChangeText={name => this.setState({ name: name })}
/>
</View>
You have two option :
You can simply change the state.name to empty string (ie : this.setState({name : ''})
Based on RN docs , You can use clear() , You have first need to get reference to your TextInput <TextInput ref={input => { this.textInput = input }} />
and then when you need to clear you text use : this.textInput.clear()

toLowerCase on TextInput value is creating duplicate text if capital letter created

This one's an interesting one.
I created a TextInput that takes a value, then lower cases it, adds it to state, and sets it as the default value. On my android physical device, if you force a capital letter ( autocapitalize is set to none), and then quickly tap other letters, it will duplicate and add extra text.
Is there a way to avoid this?
Here's a snack https://snack.expo.io/Hk1reKHJ4
Run it on your android or on the simulator, tap the upper case button on the keyboard, tap a few other letters, tap the upper case again, tap a few other letters, and you should set this error.
Thanks!
export default class App extends React.Component {
constructor(props) {
super(props)
this.state = {
text: ''
}
}
render() {
return (
<View style={styles.container}>
<TextInput
style={ styles.inputContainer }
defaultValue={ this.state.text }
autoCapitalize="none"
onChangeText={ value => this.setState({
text: value.trim().toLowerCase()
})}
/>
</View>
);
}
}
add these three lines inside TextInput, it should fix the problem, original answer source
autoCapitalize="none"
secureTextEntry={true}
keyboardType={"visible-password"}
see my this answer for example
Unfortunately this is an issue that has been open for a couple of years with no solution, you can check this thread, no one found a solution. There is a temporary workaround until the React Native team fixes this bug as it seems to be taking too long, check it out here.
The autoCapitalize prop worked for me.
autoCapitalize="none"
I do not agree with the selected answer. The link provided in it worked for me.
secureTextEntry={Platform.OS === 'ios' ? false : true}
keyboardType={Platform.OS === 'ios' ? null : 'visible-password'}
autoCapitalize="characters"
https://github.com/facebook/react-native/issues/11068#issuecomment-586346549
I made this hack:
https://snack.expo.dev/#bzozoo/duplications-at-upper-or-lowercase-async-problem-solution
Unfortunately, it is quite a compromise solution.
Input is apparently slower due to the asynchronous function
I found a solution for this issue
add toLowercase() when you posting data
axios.post("api/login", { emailid: emailid.trim().toLowerCase()})

TextInput cursor move to the beginning on reveal/hide password

I'm having a problem with TextInput cursor when reveal or hide password value, when the user touch the reveal password button the cursor move to the beginning on Android, iOS works as expected.
This is the function of reveal password:
displayPass(){
this.setState({
hiddenPass: !this.state.hiddenPass
});
}
And this is the TextInput
<TextInput onChangeText = {(pass) => this.setState({pass})}
secureTextEntry = {this.state.hiddenPass} />
And this is the button:
<TouchableOpacity onPress = {this.displayPass.bind(this)}>
<Text style = {styles.textReveal}>{this.state.hiddenPass ? "Reveal Password" : "Hide Password"}</Text>
</TouchableOpacity>
DISCLAIMER: For me, it was impossible to fix this under Expo 32. Playing with cursor position, focus/blur and setNativeProps did not solve it.
If you need a password input with hide&reveal functionality, your best options are:
Add react-native-hide-show-password-input package: https://www.npmjs.com/package/react-native-hide-show-password-input
Update expo to version 33. You can follow this instructions: https://blog.expo.io/expo-sdk-v33-0-0-is-now-available-52d1c99dfe4c (you will find the exact lists of dependencies to be updated and their compatible versions).
After the upgrade, no extra logic will be needed, just use the TextInput component. After clicking the "show/hide password" icon, you will see that the cursor stays in the correct position.

React Native: TouchableOpacity onPress problems inside a ScrollView

I am running react native 0.24.1 and I am experiencing an issue with the <TouchableOpacity> component when it is placed inside an <ScrollView>.
Its onPress events fire fine but there is a special case when they do not.
If along with the <TouchableOpacity> component you have a <TextInput>, and the current focus is on the <TextInput> box, then you may click on the <TouchableOpacity> and you will see its onPress event WILL NOT be fired.
At least the first time you do it. Once the focus is NOT on the <TextInput> anymore, you can now press on the <TouchableOpacity> component and its onPress event will fire just fine.
Note that if the <TouchableOpacity> component is placed inside a <View> instead of an <ScrollView> everything works as expected and the above issue does not apply.
Here is some code to demonstrate the problem:
const React = require('react-native');
const {
Component,
Dimensions,
View,
ScrollView,
Text,
TextInput,
TouchableOpacity,
} = React;
// ----------------------------------------------------------------------------
class TouchableOpacityTest extends Component {
constructor(props, context) {
super(props, context);
this.state = {count_onPress:0,count_onPressIn:0,count_onPressOut:0,count_onLongPress:0};
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
onPressEvent(what,e) {
console.log('what:',what);
let newState = {};
newState['count_'+what] = ++this.state['count_'+what];
this.setState(newState);
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
render() {
let touchableProps = {
onPress: this.onPressEvent.bind(this,'onPress'),
onPressIn: this.onPressEvent.bind(this,'onPressIn'),
onPressOut: this.onPressEvent.bind(this,'onPressOut'),
onLongPress: this.onPressEvent.bind(this,'onLongPress'),
}
return (
<View style={{flex:1,flexDirection:'column',justifyContent:'flex-start',alignItems:'center',backgroundColor:'blue'}} >
<ScrollView style={{width:Dimensions.get('window').width*0.9,backgroundColor:'red'}}>
<TextInput style={{backgroundColor:'rgb(200,200,200)',marginTop:14}}
placeholder="Focus on me,hide keyboard,and click on text below"
autoCorrect={false}
/>
<TouchableOpacity {...touchableProps} >
<Text style={{fontSize:20,backgroundColor:'pink',marginTop:14}}>
Click on me!{"\n"}
onPress:{this.state.count_onPress}{"\n"}
onPressIn:{this.state.count_onPressIn}{"\n"}
onPressOut:{this.state.count_onPressOut}{"\n"}
onLongPress:{this.state.count_onLongPress}{"\n"}
</Text>
</TouchableOpacity>
</ScrollView>
</View>
);
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}
// ----------------------------------------------------------------------------
AppRegistry.registerComponent('react_native_app1', () => TouchableOpacityTest);
You may replace the <ScrollView> with a <View> component on the above code and you will see that onPress event fires every time, even when the focus is on the <TextView>
NOTE: I am working on Android. I have no idea if this happens also on iOS.
NOTE 2: According to Aakash Sigdel, this is indeed happening on iOS too.
Set keyboardShouldPersistTaps={true} on your ScrollView.
Duplicate answer here: https://stackoverflow.com/a/34290788/29493
UPDATE: As Hossein writes in his answer, true|false has been deprecated in newer versions in favor of always|never|handled.
Set keyboardShouldPersistTaps='always' to your ScrollView props.
React Native Documentation:
'never' (the default), tapping outside of the focused text input when the keyboard is up dismisses the keyboard. When this happens, children won't receive the tap.
'always', the keyboard will not dismiss automatically, and the scroll view will not catch taps, but children of the scroll view can catch taps.
'handled', the keyboard will not dismiss automatically when the tap was handled by a children, (or captured by an ancestor).
false, deprecated, use 'never' instead.
true, deprecated, use 'always' instead.
In my case, I was using alignItems:'baseline', when I switched to alignItems:'center', it started working smoothly. Don't know why

Categories

Resources