React Native - text input blinking when using state - react-native

I'm adding some validation to the input text of a TextInput component.
The TextInput's value is handled in the state and is only updated when the value entered is valid.
my code looks like so:
class TextInputWithValidation extends Component {
constructor(props) {
super(props);
this.state = { text: ''}
this.handleChange = this.handleChange.bind(this);
}
handleChange(text) {
if(isValid) {
this.setState({text})
}
}
render() {
return (
<TextInput
value={this.state.text}
onChangeText={this.handleChange}
/>
)
}
}
The problem is that when entering a non valid character to the TextInput the non valid text appears for a second and the disappears - what causes an unwanted blink.
Any ideas how to solve this?

Based on your comment, a work around could be to use your handleChange method to detect for a decimal point, and then simply set some sort of inputLengthState to the current location of the decimal
Then you can use the prop for text input maxLength = this.state.inputLengthState + this.state.precision, with precision being your decimal places. Ive written some basic code below
class TextInputWithValidation extends Component {
constructor(props) {
super(props);
this.state = {
text: '',
precision: 2,
inputLength: 100,
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(text) {
if(isValid) {
this.setState({text})
}
//somewhere here you would check text for a decimal place
//and then set the inputLength the decimals' string index. If null,
//set it to some limit however you would like ( i used 100 ).
}
render() {
return (
<TextInput
value={this.state.text}
maxLength={this.state.inputLength + this.state.precision}
onChangeText={(text) => {this.handleChange(text)}}
/>
)
}
}
Apologies if my code is a bit off syntax wise, it has been a little while. For the algorithm to check for the decimal place, I'm sure this should be pretty straight forward. If not, say.

you can try to use '=>' operator
class TextInputWithValidation extends Component {
constructor(props) {
super(props);
this.state = { text: ''}
this.handleChange = this.handleChange.bind(this);
}
//here
handleChange = (text) => {
if(isValid) {
this.setState({text})
}
}
render() {
return (
<TextInput
value={this.state.text}
//and here
onChangeText={() => this.handleChange}
/>
)
}
}

Related

React Native Inline function understanding

Below are 2 patterns of code from a simple react native class component. Is there any performance difference in those? The difference is in the way a function called on an event of native control. If there is a performance difference, I want to know how to check and verify actually there is performance difference.
Pattern 1:-
class MyClass extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "",
}
}
onNameChange = (text) => {
this.setState({ name: text });
}
render() {
const { name } = this.state;
return (
<TextInput onChangeText={this.onNameChange} value={name} />
)
}
}
Pattern 2:-
class MyClass extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "",
}
}
onNameChange = (text) => {
this.setState({ name: text });
}
render() {
const { name } = this.state;
return (
<TextInput onChangeText={(text) => {
this.onNameChange(text);
}} value={name} />
)
}
}
If there is a performance difference then I need to adopt to the first pattern.
I think that there is no performance difference since the code gets transformed or minified before it gets executed.
There is no performance difference in the two patterns that you have mentioned.
First one just passes the reference to execute.
Second is just the wrapper (anonymous function) to execute your actual function.

How to map floating number from sqlite db to TextInput and backward in React Native and redux?

I'm getting a floating number from sqlite db and setting up it in redux state. Than I'm showing this property to TextInput component. To do this, I have to convert floating number to string. While editing value, I trigger event onChangeText, convert string to floating number and update redux state.
When I clear last char after point in a TextInput, my point also clearing because of converting property value from number to string. How can I save point in this case? And what's the wright way to work with floating values in react-redux?
My custom component code:
import React from 'react';
import PropTypes from 'prop-types';
import { View, TextInput } from 'react-native';
class FormFieldNumeric extends React.PureComponent {
constructor() {
super();
this.state = {
textValue: ''
}
}
componentWillReceiveProps(nextProps, nextContext) {
if (parseFloat(this.state.textValue) !== nextProps.value) {
this.setState({
textValue: nextProps.value ? String(nextProps.value) : ''
})
}
}
onChangeText = text => {
if (!text) {
this.changeValue('');
return;
}
if (text.length === 1) {
if (!'0123456789'.includes(text)) {
return;
}
}
const lastSymbol = text[text.length - 1];
if ('1234567890.,'.includes(lastSymbol)) {
if (text.split('').filter(ch => ch === '.' || ch === ',').length > 1) {
return;
}
if (lastSymbol === ',') {
this.changeValue(this.state.textValue + '.');
return;
}
this.changeValue(text);
}
};
changeValue = text => {
this.setState({
textValue: text
});
this.props.onChange(text ? parseFloat(text) : 0);
};
render() {
const { caption, value, onChange, placeholder } = this.props;
return (
<View>
<TextInput
value={this.state.textValue}
keyboardType="numeric"
onChangeText={this.onChangeText}
placeholder={placeholder}
maxLength={10}
/>
</View>
)
}
}
FormFieldNumeric.propType = {
placeholder: PropTypes.string,
value: PropTypes.number,
onChange: PropTypes.func.isRequired
};
export default FormFieldNumeric;
One option would be to only valid and update the value in your redux store when the user is finished editing vs on every keystroke. You might use the onEndEditing TextInput callback to accomplish this.
Understood what was my mistake. I fell into the anti-pattern trap when I try to keep the same state in two places. This article describes in detail this. According to the recommendations from the article, I used an uncontrolled component and stored the state directly in the component, and I only pass the property in order to initialize the value.
import React from 'react';
import PropTypes from 'prop-types';
import { View, TextInput } from 'react-native';
class FormFieldNumeric extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
defaultValue: props.defaultValue,
textValue: this.floatToTextValue(props.defaultValue)
}
}
floatToTextValue = value => {
return value ? String(value) : '';
};
render() {
const { placeholder } = this.props;
return (
<View>
<TextInput
value={this.state.textValue}
keyboardType="numeric"
onChangeText={this.onChangeText}
placeholder={placeholder}
/>
</View>
)
}
}
FormFieldNumeric.defaultValue = {
placeholder: '',
defaultValue: 0
};
FormFieldNumeric.propType = {
placeholder: PropTypes.string,
defaultValue: PropTypes.number,
onChange: PropTypes.func.isRequired
};
export default FormFieldNumeric;
And for the component to update the values ​​after loading the redux state, I made the _isLoading field in the parent component, which is true by default, but becomes false after the data is loaded. I passed this value as the key property of the parent component:
class ParentComponent extends React.PureComponent {
_isLoading = false;
async componentDidMount() {
await this.props.onCreate();
this._isLoading = false;
}
render() {
return (
<View key={this._isLoading}>
<FormFieldNumeric
defaultValue={this.props.cashSum}
onChange={this.onChangeCashSum}
placeholder="0.00"
/>
</View>
)
}
}

How can I compare two variable in onPress?

I am trying to create a changing pin screen and i was failed in comparing two variable that getting from the user (new pin and comfirm pin). The error show me that "this.state.newpin" is an undefined object.
class SettingScreen extends Component {
state = {
oldpin: '000000',
newpin: '',
secpin: ''
}
onPressButton(){
if( this.state.newpin == this.state.secpin){
ToastAndroid.show("Password Changed", ToastAndroid.SHORT);
this.setState({ oldpin : this.state.newpin})
}
else {
ToastAndroid.show("Password Unmatched", ToastAndroid.SHORT);
}
}
handleNewPin = (text) => {
this.setState({ newpin: text })
}
handleSecPin = (text) => {
this.setState({ secpin: text })
}
...
<TextInput onChangeText = {this.handleNewPin} />
<TextInput onChangeText = {this.handleSecPin} />
<TouchableOpacity onPress={this.onPressButton}>
<Text> Change Password </Text>
</TouchableOpacity>
I can get the output for "this.state.newpin" and "this.state.secpin" from user.
I just failed in the comparing statement ( OnPressButton()).
I am new in React-Native.
Sorry for any inconvenience.
you just need to bind your onPressButton()func. in the constructor with this. and move your state to constructor like this;
class SettingScreen extends Component {
constructor(props) {
super(props);
this.state = {
oldpin: '000000',
newpin: '',
secpin: ''
};
this.onPressButton = this.onPressButton.bind(this);
}
}

TextInput block change in onChange (preventDefault)?

I have to have an uncontrolled text input (for some reason not detailed here). I am trying to block a change from happening in my <TextInput>.
Here is my component and here is Snack demo of it - https://snack.expo.io/#noitsnack/textinput-block-onchange
class UncontrolledInput extends Component {
inputRef = null
render() {
return (
<View>
<TextInput ref={this.refInput} onChange={this.handleChange} />
</View>
)
}
refInput = el => this.inputRef = el
handleChange = e => {
const { nativeEvent:{ text } } = e;
// block i characters
if (text.includes('i')) {
e.preventDefault();
e.stopPropagation();
e.returnValue = false;
return false;
}
}
}
Is this possible? While preventDefault and stopPoropagation exist in e they don't seem to do anything.
You just want the text not to update in certain situations? You can always bind it to a state value and determine whether the state value should update:
class UncontrolledInput extends Component {
state = {
text: '',
}
render() {
return (
<View>
<TextInput value={this.state.text} onChangeText={this.handleChange} />
</View>
)
}
handleChange = text => {
// only update if no 'i'
if (!text.includes('i')) {
this.setState({ text });
}
}
}

Trouble with custom react component

Having a go at making my first react native component. I am trying to build a component that reverses the strings passed to it. It works perfectly fine when I give it a hard coded string, but it dosen't seem to do anything when I pass it a string from a "TextInput" component.
Here is what I think are the relevant bit of code.
class ReverseText extends Component {
constructor(props) {
super(props);
this.state = {text: props.src};
}
reverseString = (str) =>{
let newString = "";
for (let i = str.length - 1; i >= 0; i--) {
newString += str[i];
}
return newString;
};
render() {
let display = this.reverseString(this.state.text);
return (
<Text>{display}</Text>
);
}
}
export default class PizzaTranslator extends Component {
constructor(props) {
super(props);
this.state = {text: ''};
}
render() {
return (
<View style={{padding: 10}}>
<TextInput
style={{height: 40}}
placeholder="Type here to translate2!"
onChangeText={(text) => this.setState({text})}/>
<Text style={{padding: 10, fontSize: 42}}>
{/*{this.state.text.split(' ').map((word) => word && '🍕').join(' ')}*/}
{this.state.text} /* this works */
</Text>
<ReverseText src="123" /> /* this works */
<ReverseText src={this.state.text} /> /* this dosen't works */
</View>
);
}
}
Why does the component work when I pass it a hard coded string, but fails when I pass it a string that I get from the TextInput?
The class ReverseText are just setting the state at first time receive the this.props.src. So, when you change the this.props, nothing happen.
To fix you could do this:
export default class ReverseText extends Component{
constructor(props) {
super(props);
this.state = {reverseText: ''};
}
componentWillReceiveProps(nextProps){
this.setState({reverseText: this.reverseString(nextProps.src)})
}
reverseString = (str) =>{
let newString = "";
for (let i = str.length - 1; i >= 0; i--) {
newString += str[i];
}
return newString;
};
render() {
return (
<Text>{this.state.reverseText}</Text>
);
}
}