When trying to format the value and applying the state, the text in text input is flickering:

Any tips to prevent this behavior without the need to change the native code?
Using the code sample:
A1 repetitively The text input should not display the invalid character.
import React from 'react';
import { StyleSheet, Text, View, TextInput } from 'react-native';
export default class App extends React.Component {
state = {
text: ''
}
render() {
return (
<View style={styles.container}>
<TextInput
value={this.state.text}
onChangeText={text => {
text = text.replace(/[0-9]/g, '')
this.setState({
text
})
}}
style={styles.input}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
input: {
width: '90%',
height: 50,
borderColor: 'gray',
borderWidth: 1
}
});
React Native Environment Info:
System:
OS: macOS 10.14.4
CPU: (8) x64 Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz
Memory: 612.89 MB / 16.00 GB
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 10.14.1 - ~/.nvm/versions/node/v10.14.1/bin/node
Yarn: 1.12.3 - /usr/local/bin/yarn
npm: 6.9.0 - ~/.nvm/versions/node/v10.14.1/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
SDKs:
iOS SDK:
Platforms: iOS 12.2, macOS 10.14, tvOS 12.2, watchOS 5.2
Android SDK:
API Levels: 21, 23, 26, 27
Build Tools: 23.0.1, 23.0.2, 23.0.3, 25.0.0, 25.0.2, 26.0.0, 26.0.2, 26.0.3, 27.0.1, 27.0.3, 28.0.3
System Images: android-23 | Google APIs Intel x86 Atom, android-27 | Google APIs Intel x86 Atom, android-27 | Google Play Intel x86 Atom
IDEs:
Android Studio: 3.1 AI-173.4720617
Xcode: 10.2.1/10E1001 - /usr/bin/xcodebuild
npmPackages:
react: 16.5.0 => 16.5.0
react-native: https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz => 0.57.1
This the same things that happens here https://github.com/facebook/react-native/issues/23578 so closing as duplicate.
Please chime in there and help us fix it.
@thymikee I don't think it's a duplicate. The other issue specifically says it's not an issue for iOS. This issue happens on iOS for sure.
More specifically, the other issue is about a corruption of the value that ultimately ends up in state:
abcd results in AABCAABCD
In this issue, the state value is ultimately correct however for a brief period the invalid value is shown to the user.
If I had to guess its because the native code renders the text input regardless and then it's unset after the JS layer code runs. I believe that's what is happening in the componentDidUpdate of TextInput.js here.
I have same issue. I try use onKeyPress & event.preventDefault() but not work.
I believe that the solution to the problem lies in onChangeText prop
in my case i had -----> onChangeText={onChange} {...restInput}
removing {...restInput} did the job.
All the best dear reader. May you design something great :)
I can't understand how that kind of big issue still remains unresolved. Input is the most important component for almost all kind of applications and it is obviously flickering when we try to disallow some characters using regex. Input shouldn't take any action until the state given to value prop changes, so when it is controlled. It should wait the state change but it can't. A very famous library like react-native shouldn't have that kind of big bug
Isn't there a solution found yet? I also have this issue. I'll explain how I begin to have this issue. I added stack navigator to my code. After that I customized my code according to it. After that I started to get this issue.
`class LoginForm extends Component {
onEmailChange(text) {
// console.log(text);
this.props.emailChanged(text);
}
onPasswordChange(text) {
this.props.passwordChanged(text);
}
onLogin() {
const { email, password, user } = this.props;
this.props.loginUser({ email, password });
}
UNSAFE_componentWillReceiveProps(nextProps) {
console.log(nextProps.email);
}
renderButton() {
if (this.props.loading) {
return <Spinner size="large" />;
}
return (
<Button onPress={this.onLogin.bind(this)}>
Login
</Button>
);
}
render() {
return (
<Card>
<CardSection>
<Input
label="Email"
placeholder="[email protected]"
onChangeText={this.onEmailChange.bind(this)}
value={this.props.email}
secureTextEntry={false}
/>
</CardSection>
<CardSection>
<Input
secureTextEntry={true}
label="Password"
placeholder="Password"
onChangeText={this.onPasswordChange.bind(this)}
value={this.props.password}
/>
</CardSection>
<Text style={styles.errorTextStyle}>
{this.props.error}
</Text>
<CardSection>
{this.renderButton()}
</CardSection>
</Card>
);
}
}
const styles = {
errorTextStyle: {
fontSize: 20,
alignSelf: 'center',
color: 'red'
}
}
const mapStateToProps = state => {
return {
email: state.auth.email,
password: state.auth.password,
error: state.auth.error,
loading: state.auth.loading,
user: state.auth.user
}
}
export default connect(mapStateToProps, { emailChanged, passwordChanged, loginUser })(LoginForm);`
Here is my code which has the text inputs. So I added it to my App.js file like below.
`class App extends Component {
render() {
const store = createStore(reducers, {}, applyMiddleware(ReduxThunk));
return (
<Provider store={store}>
<LoginForm />
</Provider>
);
}
}
export default App;`
My text inputs were fine until this moment. After that I added stack and drawer navigators by creating another file.
`const SecondNavigator = createDrawerNavigator({
Profile: {
screen: TaskList,
navigationOptions: ({ navigation }) => ({
title: 'Your Schedule'
})
}
}, {
// drawerType: 'slide',
});
const MainNavigator = createStackNavigator({
// Open: { screen: OpenWindow },
Home: {
screen: LoginForm,
navigationOptions: ({ navigation }) => ({
title: 'Sign In',
headerStyle: { backgroundColor: '#0680EC', height: 45 },
headerLeft: null,
headerRight: null
})
},
Profile: {
screen: SecondNavigator,
navigationOptions: ({ navigation }) => ({
title: 'Your Schedule',
headerStyle: { backgroundColor: '#0680EC', height: 45 },
headerLeft:
headerRight:
})
}
});
const Router = createAppContainer(MainNavigator);
const mapStateToProps = state => {
return {
email: state.auth.email,
password: state.auth.password,
error: state.auth.error,
loading: state.auth.loading,
user: state.auth.user
}
}
export default connect(mapStateToProps, { emailChanged, passwordChanged, loginUser, logOutUser })(Router);`
Then I added this file to the App.js since login form is already imported in this file.
`class App extends Component {
render() {
const store = createStore(reducers, {}, applyMiddleware(ReduxThunk));
return (
<Provider store={store}>
<Router />
</Provider>
);
}
}
export default App;`
This is when I started to have this issue. Navigation was fine. Every other operation works fine. But text input is Flickering.
If anyone has an idea of how to solve it, please advise.
I also have this issue und would love it to be solved or at least learn about a workaround! Cheers
I think I found an answer to this issue, and almost due to sheer luck, I must say. The answer is short: NEVER use value to render the value of TextInput. Use defaultValue instead. Tada! That solved my flickering problem! =D
In the example posted by OP, render function should have been:
render() {
return (
<View style={styles.container}>
<TextInput
defaultValue={this.state.text} // -> Here! This is all we need to modify.
onChangeText={text => {
text = text.replace(/[0-9]/g, '')
this.setState({
text
})
}}
style={styles.input}
/>
</View>
);
}
The flickering when trying to prevent certain characters from being inputted is still a problem for me on 0.61.4. I tried @lgenzelis idea, but that doesn't work, my regex code is ignored. I get this on both iOS and Android. Could this ticket be reopened?
import React, {useState} from 'react';
import {
TextInput,
NativeSyntheticEvent,
TextInputChangeEventData,
} from 'react-native';
const NUMBERS_ONLY_REGEX = RegExp(/^(\d*(\.\d{0,2})?)?$/);
const MyTextInput: React.FC = props => {
const [value, setValue] = useState();
const onChangeHandler = (
e: NativeSyntheticEvent<TextInputChangeEventData>,
) => {
const text = e.nativeEvent.text;
if (!isValidInput(text)) {
e.preventDefault();
} else {
setValue(text);
}
};
return (
<TextInput
style={{height: 40, borderColor: 'gray', borderWidth: 1}}
onChange={onChangeHandler}
value={value}
/>
);
};
const isValidInput = (input: string): boolean =>
NUMBERS_ONLY_REGEX.test(input.replace(/,/g, ''));
export default MyTextInput;
God damn.. this was an issue back when I tried react native in 2015.. cant believe it's still an issue today..
@thymikee Can you please reopen this issue? It's not the same issue as the one you referenced. This bug is a flickering that happens when you try to prevent a character from being displayed in the input onChangeText. The other issue is some android only bug with formatting of uppercase/lowercase letters..
@timurridjanovic the original issue is about flickering when formatting the text, there's no preventing default in the OP. Feel free to create a new issue with a detailed reproduction.
Most helpful comment
I can't understand how that kind of big issue still remains unresolved. Input is the most important component for almost all kind of applications and it is obviously flickering when we try to disallow some characters using regex. Input shouldn't take any action until the state given to value prop changes, so when it is controlled. It should wait the state change but it can't. A very famous library like react-native shouldn't have that kind of big bug