TextInput value prop not taking proper state value - react-native

I am trying to build an input field which will only take numbers as input. Minimal component definition to explain my problem is as below
type Props = {};
export default class App extends Component<Props> {
constructor(props) {
super(props);
this.state = {
text: 'PlaceHolder'
}
}
sanitizeInput(input) {
let sanitizedInput = input.replace(/[^0-9]/g, '');
this.setState({text: sanitizedInput});
}
render() {
console.log("In render - ", this.state.text);
return (
<View style={{flex: 1, justifyContent: 'center'}}>
<TextInput
style={{height: 40, borderColor: 'gray', borderWidth: 1}}
onChangeText={(text) => this.sanitizeInput(text)}
value={this.state.text}
/>
</View>
);
}
}
But, when I am executing it, I am not getting the desired result. It seems that the TextInput is not respecting the value prop passed into it. The console.log is clearly showing desired value to be shown in TextInput, but I am not able to get that value properly in TextInput of device.
A video describing my problem is posted here

Related

how to stop images from rendering on setState

I have images associated with a counter and based on this increment or decrement in counter, a calculation is done and displayed in a text at the bottom.
The problem is that when I render, the images get rendered again and are loaded again and again and again. which I dont want.
If I dont render, the text will not update with the calculated amount.
For the counter I am using react-native-counter.
I have already tried with shouldcomponentupdate, but I want to stop only image rendering, the rest should work.
Please advise.
export default class App extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<Header
backgroundColor="#25D366"
leftComponent={
<Icon
name="menu"
size={40}
color={"#fff000"}
onPress={() => this.props.navigation.openDrawer()}
/>
}
centerComponent={{
text: "Veg & Fruits",
style: {
color: "#ffffff",
fontSize: 25,
fontWeight: "bold",
},
}}
rightComponent={<Icon name="home" color={"#ff0000"} />}
></Header>
/// this is searchbar component,
<SearchBar
fontColor="#ffffff"
fontWeight="bold"
fontSize={20}
iconColor="#c6c6c6"
shadowColor="#ffffff"
cancelIconColor="#c6c6c6"
backgroundColor="#25D366"
placeholder="Search here"
onChangeText={(text) => {
this.setState({ photos: [] });
this.state.search = text;
this.filterList(this.state.search);
console.log("text changed");
}}
onPressCancel={(text) => {
text = "";
//this.filterList(text);
}}
onPress={(text) => {
console.log("rendering");
console.log("now text is: ", this.state.search);
}}
/>
/// in this view images are displayed using functions
<View>
<ScrollView
style={{
height: Dimensions.get("window").height - 200,
}}
>
<View
key={Date.now()}
style={{
flex: 1,
flexDirection: "column",
flexWrap: "wrap",
}}
>
{this.filterList(this.state.search)}
{this._renderImages()}
</View>
</ScrollView>
<CalcText tt={total_num} />
</View>
</div>
);
}
}
class CalcText extends Component {
constructor(props) {
super(props);
this.state = {
ta: 0,
};
}
shouldComponentUpdate(nextProps) {
console.log(nextProps.tt);
if (this.props.tt !== nextProps.tt) {
console.log("changed");
return true;
} else {
console.log("Not changed");
return false;
}
}
render() {
return (
<View style={{ height: 40, backgroundcolor: "ff0000" }}>
<Text style={{ fontSize: 26, fontWeight: "bold" }}>
Total : {this.props.tt}
</Text>
</View>
);
}
}
You can create a TextBox component and split it from ImageComponent. In this way the images will not be bound to the state of the component rendering text, and you can safely change TextBox state and text preventing ImageComponent to re-render.
Edit:
Okei, now i get it. I think you have no possibility to do it like this.
Let's formalize the problem:
<Parent>
<Images calc={functionToCalc} />
<CalcText totalAmount={this.state.totalAmount}/>
</Parent>
functionToCalc is defined in in <Parent> and update parent state, something like:
const funcToCalc = () => {
// when condition occurs
this.setState({
totalAmount : computedAmount
})
}
The state of <Parent> has:
this.state : {
totalAmount: none
}
Whenever condition (buttonClick?) occurs in <Images/> you run functionToCalc update <Parent> state and rerender <CalcText>. Problem here is that also <Images> will be rerender again as all the parent component will be rerender.
this is one of the way to pass info from siblings in React.
You only have a possibility if you want to prevent <Images> rerendering.
Redux or similar libraries for centralize state
You will move the computed calculation in a Store and <CalcText/> will read that from there. <Images/> component will trigger an action modifying that state but won't listen to that so not being rerender.

Get user input from input field in react similar to getElementById in react native using props

I am doing a loan calculation app and i run into the trouble since i am new to react native and previously i have been manipulating the DOM using querySelector or getElementById functions. However this does not work in react, and i am using state to store the value from the user, but i just can't seem to get it right, What am i doing wrong?
I've inserted the calculation element that is later rendered in app.js. All elements are showing up with no error, but the problem is to get user input data and then be able to use that data and do calculations.
Here is my Class
class LanKalkylElement extends React.Component {
constructor(props) {
super(props);
this.state = {
loanAmount: 20000,
loanInterest: 2.5,
loanYear: 10,
};
}
changeAmount(loanAmount) {
this.setState(() => {
return {
loanAmount: parseFloat(loanAmount),
};
});
}
changeInterest(loanInterest) {
this.setState(() => {
return {
loanInterest: parseFloat(loanInterest),
};
});
}
changeYear(loanYear) {
this.setState(() => {
return {
loanYear: parseFloat(loanYear),
};
});
}
calcButton() {
Alert.alert(this.props.loanAmount);
}
buttonHomeFunc() {
this.props.navigation.navigate('Start');
}
render() {
const {loanAmount, loanInterest, loanYear} = this.state;
return(
<View style={styles.contentStyle}>
<Text style={styles.text}> Lånebelopp </Text>
<TextInput style={styles.numericInput}
onBlur={Keyboard.dismiss}
keyboardType={'numeric'}
value={loanAmount}
onValueChange={this.changeAmount.bind(this)} />
<Text style={styles.text}> Ränta </Text>
<TextInput style={styles.numericInput}
onBlur={Keyboard.dismiss}
keyboardType={'numeric'}
value={loanInterest}
onValueChange={this.changeInterest.bind(this)} />
<Text style={styles.text}> Antal år: {String(loanYear)}</Text>
<Slider step={1}
maximumValue={15}
value={loanYear}
onValueChange={this.changeYear.bind(this)} />
<Button title='Kalkylera' onPress={() => this.calcButton()}/>
<Text style={styles.textResult}>Total summa att återbetala:</Text>
<Text style={styles.textResult}>varav räntekostnad:</Text>
<Button title='Tillbaka' onPress={() => this.buttonHomeFunc()}/>
</View>
)
}
}
export default withNavigation(LanKalkylElement);
When a user changes a value in a text input, onValueChange is called. You have bound this prop to functions that modify the state for this component.
This means the value in the text input will always match the value in the state. Therefore, if you need to access the value in a text input you would simply retrieve it from the state, like this:
const loanAmount = this.state.loanAmount;
doSomethingWithLoanAmount(loanAmount);

how to show count increment value on icon on next page after clicking from first page button

class ContentPage extends Component {
constructor(){
super()
this.state = {
data: '',
count: 0 ,
}
this.handleClick = this.handleClick.bind(this)
this.deletehandle = this.deletehandle.bind(this)
}
handleClick(){
this.setState(prevState => {
return {
count: prevState.count + 1
}
})
}
deletehandle(){
if (this.state.count === 0) {
this.setState({
counter: 0
});
} else {
this.setState(prevState => ({
count: this.state.count - 1
}));
}
}
this is my increment and decrement code on first page and after on press handleclicked it increases and the state value i want to show is on next page on icon
i have used
{this.state.count}
</View>
<View style={{alignItems:'flex-end',alignContent:'flex-end',marginLeft:260}}>
<View style={{flexDirection:'row'}}>
<Text style={{fontSize: 18}}> Quantity : </Text><Text style={{fontSize:18}}>{this.state.count}</Text>
</View>
</View>
<View style={{height: 4, width: 30}} />
<TouchableOpacity
// onPress={this._AddCart}
onPress={this.handleClick}
style={{
height: 40,
width: 130,
backgroundColor: 'orange',
justifyContent: 'center',
alignItems: 'center',
borderRadius:8
}}>
<Text style={{color:"#FFFFFF",fontSize:15}} >Add to Cart{this.state.count}</Text>
</TouchableOpacity>
edited my code now showing my count state in the same page but i want to show my count numbers on next screen ?
This example is just focusing on how to pass the data as props from the parent to the child component. So please ignores the syntax and other issues.
// Parent Component
class ParentComponent extends Component {
// Pass count to Child Component as props
<ChildComponent count={this.state.count}/>
}
// Child Component
class ChildComponent extends Component {
// call count in child component using props
this.props.count
}
You need to pass your count property down to the component where you want to use it (exactly like #Sourav Singh has shown in his answer). I composed an increment/decrement example for you, hope you find it helpful:
https://codesandbox.io/s/react-typescript-429re
Also, you might find some interesting insights in Components and Props article from official React documentation.

react-native: undefined is not an object (evaluating 'prop.slice')

im new to coding, and React-Native, and running into this error which i can't find an answer to, after extensive googling (it might be due to my newbiness and not understanding answers given to other users, so please be patient and point me in the right direction, if thats the case)
the error is:
undefined is not an object (evaluating 'timeAsProp.slice')
so to explain what timeAsProp.slice is: basically i needed the time to be passed as a prop to the newOrder() method, in just two digits. if the time is 10:00:00, my newOrder method should just proccess: 10
the current time is stored in state.
currentTime: new Date().toLocaleTimeString()
the above current time state reads out on the emulator as:
12:00:00 //if the time was 12:00 ocklock
The function
newOrder(timeAsProp)
recieves the time, and depending on what the hour is, should return a different array of images to be rendered for each hour on the hour.
(i only put two time options hardcoded into the app as im still building it. afterwards there will be a seperate image order returned, for each hour. its hardcoded this way, with a seperate option for each hour, untill i can figure out how to have a loop automatically set the images in their proper order depending on the time)
so basically, i need a different way of doing this, or figuring out why im getting this error...
thnks in advance ;)
heres my code:
import React, { Component } from 'react';
import { View, Text, Image, StyleSheet } from 'react-native';
import AwesomeButton from 'react-native-awesome-button';
import Style from './Style';
class App extends Component {
constructor(props) {
super(props);
this.state = {
currentTime: null,
};
}
componentDidMount() {
setInterval(() => {
this.setState({
currentTime: new Date().toLocaleTimeString()
});
}, 1000);
}
handleButtonPress() {
}
**// heres the timeAsProp.slice which is giving me the error:**
newOrder(timeAsProp) {
const hour = timeAsProp.slice(0, 2);
if (hour === '19') {
return [
<div>
<Image source={require('./img/hey2.png')} style={Style.image} />
<Image source={require('./img/vav.png')} style={Style.image} />
<Image source={require('./img/hey1.png')} style={Style.image} />
<Image source={require('./img/yud.png')} style={Style.image} />
</div>
];
}
if (hour === '20') {
return [
<div>
<Image source={require('./img/vav.png')} style={Style.image} />
<Image source={require('./img/hey2.png')} style={Style.image} />
<Image source={require('./img/hey1.png')} style={Style.image} />
<Image source={require('./img/yud.png')} style={Style.image} />
</div>
];
}
}
render() {
return (
<View style={Style.rootContainer}>
<View style={Style.headerContainer}>
<Text style={styles.blue}> {this.state.curentTime} </Text>
</View>
<View style={Style.displayContainer}>
**//this is whats calling newOrder and returning the images**
{this.newOrder(this.state.time)}
</View>
<View style={Style.buttonContainer} >
<AwesomeButton
states={{
default: {
text: 'DeeDee! Dont press da button!',
backgroundStyle: {
backgroundColor: 'blue',
minHeight: 45,
alignItems: 'center',
borderRadius: 30,
marginBottom: 15,
marginLeft: 15,
},
onPress: this.handleButtonPress
}
}}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
blue: {
fontFamily: 'serif',
color: 'blue',
fontWeight: 'bold',
fontSize: 30,
}
});
export default App;
Thanks for taking a look and hopefully teaching me something new!
sb
{this.newOrder(this.state.time)} is what you're calling in the render method but the value you have in state is currentTime:
this.state = {
currentTime: null,
};
Make sure you're passing the right variable in to the method you're calling. Also, it's better to check in the method that the variable has the type and value you expect before using it. So inside of newOrder perhaps check if timeAsProp is type string (typeof timeAsProp === 'string') before calling slice on it.
First, you have to change
{this.newOrder(this.state.time)}
To
{this.newOrder(this.state.currentTime)}
Then, in your constructor, initialize currentTime
constructor(props) {
super(props);
this.state = {
currentTime: new Date().toLocaleTimeString(),
};
}
The reason is that, as stated in the docs, render() executes before componentDidMount(), so in the moment newOrder is being called, currentTime has not been initialized.

setNativeProps Change Value for Text Component React Native Direct Manipulation

I want to directly update the value of a component due to performance reasons.
render(){
<View>
<Text style={styles.welcome} ref={component => this._text = component}>
Some Text
</Text>
<TouchableHighlight underlayColor='#88D4F5'
style={styles.button}>
<View>
<Text style={styles.buttonText}
onPress={this.useNativePropsToUpdate.bind(this)}>
Iam the Child
</Text>
</View>
</TouchableHighlight>
</View>
}
This is the method I use to update the text component. I dont know if I am setting the right attribute/ how to figure out which attribute to set:
useNativePropsToUpdate(){
this._text.setNativeProps({text: 'Updated using native props'});
}
Essentially trying to follow the same approach from this example:
https://rnplay.org/plays/pOI9bA
Edit:
When I attempt to explicitly assign the updated value:
this._text.props.children = "updated";
( I know this this the proper way of doing things in RN ). I get the error "Cannot assign to read only property 'children' of object'#'"
So maybe this is why it cant be updated in RN for some reason ?
Instead of attempting to change the content of <Text> component. I just replaced with <TextInput editable={false} defaultValue={this.state.initValue} /> and kept the rest of the code the same. If anyone know how you can change the value of <Text> using setNativeProps OR other method of direct manipulations. Post the answer and ill review and accept.
The text tag doesn't have a text prop, so
this._text.setNativeProps({ text: 'XXXX' })
doesn't work.
But the text tag has a style prop, so
this._text.setNativeProps({ style: { color: 'red' } })
works.
We can't use setNativeProps on the Text component, instead, we can workaround and achieve the same result by using TextInput in place of Text.
By putting pointerEvent='none' on the enclosing View we are disabling click and hence we can't edit the TextInput (You can also set editable={false} in TextInput to disbale editing)
Demo - Timer (Count changes after every 1 second)
import React, {Component} from 'react';
import {TextInput, StyleSheet, View} from 'react-native';
class Demo extends Component {
componentDidMount() {
let count = 0;
setInterval(() => {
count++;
if (this.ref) {
this.ref.setNativeProps({text: count.toString()});
}
}, 1000);
}
render() {
return (
<View style={styles.container} pointerEvents={'none'}>
<TextInput
ref={ref => (this.ref = ref)}
defaultValue={'0'}
// editable={false}
style={styles.textInput}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 0.7,
justifyContent: 'center',
alignItems: 'center',
},
textInput: {
fontSize: 60,
width: '50%',
borderColor: 'grey',
borderWidth: 1,
aspectRatio: 1,
borderRadius: 8,
padding: 5,
textAlign: 'center',
},
});
export default Demo;
As setNativeProps not solving the purpose to alter the content of <Text />, I have used below approach and is working good. Create Simple React Component like below...
var Txt = React.createClass({
getInitialState:function(){
return {text:this.props.children};
},setText:function(txt){
this.setState({text:txt});
}
,
render:function(){
return <Text {...this.props}>{this.state.text}</Text>
}
});