How can I compare two variable in onPress? - react-native

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);
}
}

Related

Force external component to re-render with new props

I am trying to set up e-mail sign-up. I have a screen with a TextInput, which I want to reuse. I have an EmailConnector from which I navigate to the TextInputScreen. This TextInputScreen containt a TextInputComponent. Here, the user enters his email. If the email is invalid, I throw an error and try to update the error message in my TextInputScreen.
The problem that I am facing right now is that the error message from my TextInputComponent is not updated when an error is thrown.
The flow is this:
The user taps on "Sign-up with email" in a separate screen -> openEmailScreen is called
The user inputs an email and taps on the keyboard "Done" button -> inputReceived is called
If the email is invalid -> an error is thrown in inputReceived and the error message is displayed in TextInputViewComponent
Refreshing the errror message in step #3 is NOT working at the moment and I can't figure out how to get it working.
This is my EmailConnector:
export default class EmailConnector {
static keyboardTypes = {
email: 'email-address',
default: 'default',
};
static openEmailScreen = async navigation => {
navigation.navigate('TextInputScreen', {
placeholder: strings.onboarding.email_flow.email_placeholder,
keyboardType: this.keyboardTypes.email,
onKeyboardPressed: () => this.inputReceived(),
errorMessage: 'placeholder message',
})
}
//method called when the "Done" button from the keyboard is pressed
static inputReceived = () => {
try {
const email = new SignUpUserBuilder().setEmail('testexample.com').build();//used to validate the email
}
catch(error) {
console.log(error);
****//HERE I need to figure out a way to change props.errorMessage and force TextInputViewComponent to rerender****
<TextInputViewComponent errorMessage = 'Invalid email'/>;
const viewComponent = new TextInputViewComponent();
viewComponent.forceUpdate();
}
}
}
This is my TextInputScreen:
class TextInputScreen extends Component {
render() {
return (
<View style={styles.rootView}>
<TextInputViewComponent
placeholder={this.props.route.params.placeholder}
keyboardType={this.props.route.params.keyboardType}
onKeyboardPressed={this.props.route.params.onKeyboardPressed}
errorMessage={this.props.route.params.errorMessage}
/>
</View>
)
}
}
export default TextInputScreen;
And this is my TextInputViewComponent:
class TextInputViewComponent extends Component {
constructor(props) {
super(props);
this.state = {
shouldRefreshComponent: false
}
}
refreshComponent = () => {
this.setState({
shouldRefreshComponent: true
})
}
render() {
return (
<View>
<TextInput
placeholder={this.props.placeholder}
placeholderTextColor={colors.placeholder}
keyboardType={this.props.keyboardType}
style={styles.textInput}
onSubmitEditing= {() => this.props.onKeyboardPressed()}
/>
<Text
style={{fontSize: 18, color: colors.white}}
ref={Text => { this._textError = Text}}>
{this.props.errorMessage}
</Text>
</View>
)
}
}
export default TextInputViewComponent;
From the inputReceived method, I have tried calling forceUpdate and setState for the TextInputViewComponent, but in both cases I get the message: "Can't call forceUpdate/setState on a component that is not yet mounted"
Any help will be greatly appreciated!
In general terms if I want a parent component to mutate it's data or update when a child component changes I pass the child a function to its props.
For example
class Parent {
state = {
value: 1
}
updateValue() {
this.setState({value: 2})
}
render() (
<Child
updateValue={this.updateValue}
/>
)
That way I can call the function inside the child to update the parent's state.
class Child {
updateParent() {
this.props.updateValue()
}
}

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 do I clear placeholder text when using a ref in React Native?

I have a TextInput component gets reused in a few different places:
export default class SomeTextInput extends Component {
constructor(props) {
super(props);
}
render() {
let fontWeight = this.props.fontWeight ? this.props.fontWeight : 'Light';
let fontName = this.props.fontName ? this.props.fontName : 'Montserrat';
let fontString = createFontString(fontName, fontWeight);
let applyFontFamily = { fontFamily: fontString };
let style = this.props.style.constructor === Array ? this.props.style : [this.props.style];
return (
<TextInput
ref={(ref) => {
this.textInput = ref
}}
{...this.props}
style={[applyFontFamily, ...style]}
onFocus={() => {
this.clearText();
console.log('show me this.textInput', this.textInput.props.placeholder)
}}
/>
)
}
clearText() {
this.textInput.clear();
console.log('is this being reached???')
}
focus() {
this.textInput.focus();
}
blur() {
this.textInput.blur();
}
}
I've also tried using clearTextOnFocus. I believe the best way to do this would be to change the placeholder to '', but I'm not sure how given that the placeholder text is taken from a prop that's been passed down.
edit: I'm going to add the code that #ravibagul91 suggested
export default class OVTextInput extends Component {
constructor(props) {
super(props);
// this.state = {
// placeholder: props.placeholder
// }
}
render() {
let fontWeight = this.props.fontWeight ? this.props.fontWeight : 'Light';
let fontName = this.props.fontName ? this.props.fontName : 'Montserrat';
let fontString = createFontString(fontName, fontWeight);
let applyFontFamily = { fontFamily: fontString };
let style = this.props.style.constructor === Array ? this.props.style : [this.props.style];
return (
<TextInput
ref={(ref) => {
this.textInput = ref
}}
{...this.props}
style={[applyFontFamily, ...style]}
onFocus={() => {
// this.setState({ placeholder: "" });
this.clearText();
}}
/>
)
}
clearText = () => {
console.log(this.textInput)
console.log('is this being reached???', this.textInput.value);
console.log('is this being reached???', this.textInput.placeholder);
this.textInput.placeholder = "";
this.textInput.value = "";
}
// focus = () => {
// this.textInput.focus();
// }
// blur = () => {
// this.textInput.blur();
// }
focus() {
this.textInput.focus();
}
blur() {
this.textInput.blur();
}
};
What you are currently doing is erasing the value of the text. Your Textinput looks like a prop for receiving and using values. Textinput does not currently have the ability to clear placeholders. If you make a proposal, you can use the status values to solve it.
export default class SomeTextInput extends Component {
constructor(props) {
super(props);
this.state={
placeholder: props.placeholder
}
}
....
<TextInput
ref={(ref) => {
this.textInput = ref
}}
placeholder={this.state.placeholder}
{...this.props}
style={[applyFontFamily, ...style]}
onFocus={() => {
this.setState({ placeholder : "" });
console.log('show me placeholder', this.state.placeholder)
}}
/>
You can directly clear the placeholder like,
this.textInput.placeholder = "";
Demo
Note: This is tested simply on input but same will work for TextInput.

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 });
}
}
}

React Native - text input blinking when using state

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}
/>
)
}
}