I am new to react-native and trying to make a GSTCalculator App. I take values from the user on calculator screen and show calculated values on Result screen. My problem is that the calculated values don't show up immediately on the Result screen. I have even used async and await. But it only shows first calculated value.
calculate = async() => {
await this.calculateSum();
this.props.navigation.navigate('Result', {prodCost: this.state.prodCost,
mfdProfitMargin: this.state.mfdProfitMargin, mCost: this.state.mCost, whProfit: this.state.whProfit,
rtProfit: this.state.rtProfit, cgst: this.state.cgst, sgst: this.state.sgst, igst: this.state.igst,
igstV: this.state.igstV, m2wV: this.state.m2wV, w2rT: this.state.w2rT, whProfitV: this.state.whProfitV,
cgstV: this.state.cgstV, sgstV: this.state.sgstV, w2rV :this.state.w2rV, rtProfitV: this.state.rtProfitV })
}
calculateSum = () => {
this.setState({mCost: Number(this.state.prodCost) + Number(this.state.mfdProfitMargin)});
this.setState({igstV: (Number(this.state.mCost)*Number(this.state.igst))/100});
this.setState({m2wV: Number(this.state.mCost) + Number(this.state.igstV)});
this.setState({whProfitV: (this.state.m2wV)*Number(this.state.whProfit)/100});
this.setState({w2rT: Number(this.state.m2wV) + Number(this.state.whProfitV)});
this.setState({cgstV: (this.state.w2rT)*Number(this.state.cgst)/100});
this.setState({sgstV: (this.state.w2rT)*Number(this.state.sgst)/100});
this.setState({w2rV: Number(this.state.w2rT) + Number(this.state.cgstV) + Number(this.state.sgstV)});
this.setState({rtProfitV: (this.state.w2rV)*Number(this.state.rtProfit)/100});
}
export default class Result extends Component {
constructor(props) {
super(props);
this.state = { pCost: this.props.navigation.state.params.prodCost, pMargin: this.props.navigation.state.params.mfdProfitMargin,
mCost:this.props.navigation.state.params.mCost, igstP: this.props.navigation.state.params.igst,
igstV: this.props.navigation.state.params.igstV, cgstP: this.props.navigation.state.params.cgst,
cgstV: this.props.navigation.state.params.cgstV, sgstP: this.props.navigation.state.params.sgst,
sgstV: this.props.navigation.state.params.sgstV, m2wV: this.props.navigation.state.params.m2wV,
whProfit: this.props.navigation.state.params.whProfit, whProfitV: this.props.navigation.state.params.whProfitV,
w2rT: this.props.navigation.state.params.w2rT, w2rV: this.props.navigation.state.params.w2rV,
rtProfit:this.props.navigation.state.params.rtProfit, rtProfitV: this.props.navigation.state.params.rtProfitV
}
}
render() {
return(
<View>
<ImageBackground source={require('../Assets/login.jpg')} style={Styles.bgImage}>
<ScrollView contentContainerStyle={GStyle.container}>
<View>
<Text style={[Styles.heading, {marginTop: 20}]}>Manufacturer to Wholesaler:</Text>
<View style={GStyle.inputView}>
<Text style={[GStyle.text, {marginTop:10}]}>Production Cost</Text>
<Text style={GStyle.resultText}>{this.state.pCost}</Text>
</View>
<View style={GStyle.inputView}>
<Text style={GStyle.text}>Profit Margin</Text>
<Text style={GStyle.resultText}>{this.state.pMargin}</Text>
</View>
<View style={GStyle.inputView}>
<Text style={GStyle.text}>Manufacture Cost</Text>
<Text style={GStyle.resultText}>{this.state.mCost}</Text>
</View>
<View style={GStyle.inputView}>
<Text style={GStyle.text}>IGST({this.state.igstP}%)</Text>
<Text style={GStyle.resultText}>{this.state.igstV}</Text>
</View>
<View style={GStyle.inputView}>
<Text style={GStyle.text}>Invoice Value</Text>
<Text style={GStyle.resultText}>{this.state.m2wV}</Text>
</View>
</View>
<View>
<Text style={[Styles.heading, {marginTop: 20}]}>Wholesaler to Retailer:</Text>
<View style={GStyle.inputView}>
<Text style={GStyle.text}>Cost to Wholesaler</Text>
<Text style={GStyle.resultText}>{this.state.m2wV}</Text>
</View>
<View style={GStyle.inputView}>
<Text style={GStyle.text}>Profit Margin({this.state.whProfit}%)</Text>
<Text style={GStyle.resultText}>{this.state.whProfitV}</Text>
</View>
<View style={GStyle.inputView}>
<Text style={GStyle.text}>Total</Text>
<Text style={GStyle.resultText}>{this.state.w2rT}</Text>
</View>
<View style={GStyle.inputView}>
<Text style={GStyle.text}>CGST({this.state.cgstP}%)</Text>
<Text style={GStyle.resultText}>{this.state.cgstV}</Text>
</View>
<View style={GStyle.inputView}>
<Text style={GStyle.text}>SGST({this.state.sgstP}%)</Text>
<Text style={GStyle.resultText}>{this.state.sgstV}</Text>
</View>
<View style={GStyle.inputView}>
<Text style={GStyle.text}>Invoice Value</Text>
<Text style={GStyle.resultText}>{this.state.w2rV}</Text>
</View>
</View>
<View>
<Text style={[Styles.heading, {marginTop: 20}]}>Retailer to Consumer:</Text>
<View style={GStyle.inputView}>
<Text style={GStyle.text}>Cost to Retailer</Text>
<Text style={GStyle.resultText}>{this.state.w2rV}</Text>
</View>
<View style={GStyle.inputView}>
<Text style={GStyle.text}>ProfitMargin({this.state.rtProfit}%)</Text>
<Text style={GStyle.resultText}>{this.state.rtProfitV}</Text>
</View>
</View>
</ScrollView>
</ImageBackground>
</View>
);
}
}
There is a lot of setting of state going I think we can reduce it and there is a little refactoring we can do to make your code easier to read.
Constructor
You are typing the same thing over and over again and it makes your constructor much harder to read than it needs to be.
In this example we have taken the the value this.props.navigation.state.params and set it to it's own variable. This means you don't have to keep typing the this.props.navigation.state.params over and over and over again.
constructor(props) {
super(props);
const params = this.props.navigation.state.params
this.state = {
pCost: params.prodCost,
pMargin: params.mfdProfitMargin,
mCost:params.mCost,
igstP: params.igst,
igstV: params.igstV,
cgstP: params.cgst,
cgstV: params.cgstV,
sgstP: params.sgst,
sgstV: params.sgstV,
m2wV: params.m2wV,
whProfit: params.whProfit,
whProfitV: params.whProfitV,
w2rT: params.w2rT,
w2rV: params.w2rV,
rtProfit:params.rtProfit,
rtProfitV: params.rtProfitV
}
}
If we use spread operator we can make this even easier, that is if you are willing to use the same name as in the object that you are passing.
constructor(props) {
super(props);
this.state = {
...this.props.navigation.state.params
}
}
However, if you use this method then you will find that some of the unique names you have chosen will be gone, replaced by the ones that are in the object that you are passing.
I am assuming that you would go with the first option (as it doesn't change the names) so I will base my next refactors on that.
Calculate Sum
There are too many setStates here, It takes time for state to set, so you could be using old values in your subsequent calculations so it is best not to use setState until you have performed all your calculations. If the values for each setState depend on the previous values then you could do something like this, only using values from the initial state where necessary.
calculateSum () => {
let mCost = Number(this.state.prodCost) + Number(this.state.mfdProfitMargin);
let igstV = mCost * Number(this.state.igst) / 100;
let m2wV = mCost + igstV;
let whProfitV = m2wV * Number(this.state.whProfit)/100;
let w2rT = m2wV + whProfitV;
let cgstV = w2rT * Number(this.state.cgst)/100;
let sgstV = w2rT * Number(this.state.sgst)/100;
let w2rV = w2rT + cgstV + sgstV;
let rtProfitV = w2rV * Number(this.state.rtProfit)/100;
return { mCost, igstV, m2wV, whProfitV, w2rT, cgstV, sgstV, w2rV, rtProfitV }
}
Here I return an object with all the updated values, that can be saved to state if we so choose.
Calculate
As the calculateSum function now returns an object with all the value that you want you could do something like this. We take the sum object that we just calculated and using a spread operator set all those objects to state. We then use a callback on the setState function to then navigate to the next page, using the spread operator again to capture all the values that in state.
calculate = () => {
let sum = this.calculateSum();
this.setState({...sum}, () => {
this.props.navigation.navigate('Result', {...this.state })
});
}
However if these values don't need to saved to state and just passed to the next screen, we could update the calculate function to something like this
calculate = () => {
let sum = this.calculateSum();
this.props.navigation.navigate('Result', {...this.state, ...sum })
}
I hope this helps you.
For more information on the spread operator in javascript see this article
https://zendev.com/2018/05/09/understanding-spread-operator-in-javascript.html
For more information on setting state check out both of these articles
https://medium.learnreact.com/setstate-is-asynchronous-52ead919a3f0
https://medium.learnreact.com/setstate-takes-a-callback-1f71ad5d2296
I'm trying to create custom button. For this, I wrapped my existed view into TouchableHighlight (write me please another way, if it's not suitable here)
<TouchableHighlight onPress={this.freeTimeTapped} underlayColor="white">
<LabelsView data={this.freeTimeData}
containerStyle={{ backgroundColor: '#3A65FF' }} />
</TouchableHighlight>
This code throws an error touchable child must either be native, described here, for example. So, I added
setNativeProps = (nativeProps) => {
this._root.setNativeProps(nativeProps);
}
error disappeared, but now I receive an error
React Native Error: undefined is not an object (evaluating
'_this._root.setNativeProps')
after touch. What am I doing wrong?
More code about LabelsView:
export default class LabelsView extends Component {
// Make TouchableHighlight wrapper work
setNativeProps = (nativeProps) => {
this._root.setNativeProps(nativeProps);
}
render() {
return (
<View style={[styles.container, this.props.containerStyle]}>
<View style={styles.leftContainer}>
<Text style={[styles.nameText, styles.textColor]}> {this.props.data.leftText} </Text>
</View>
<View style={styles.rightContainer}>
<Text style={[styles.durationText, styles.textColor]}> {this.props.data.rightTopText + ' hrs'} </Text>
<Text style={[styles.rangeText, styles.textColor]}> {this.props.data.rightBottomText} </Text>
</View>
</View>
);
}
}
I created the same sitaution as yours and found that the only thing you are doing wrong is you are wrapping a class inside the TouchableHighlight . If you want to wrap it in any touchable component then react native needs the native child, So to resolves this change your code as follows:-
<LabelsView freeTimeTapped={this.freeTimeTapped} data={this.freeTimeData}
containerStyle={{ backgroundColor: '#3A65FF' }} />
and your LabelsView class as follows:-
render() {
return (
<TouchableHighlight onPress={this.props.freeTimeTapped} underlayColor="white">
<View style={[styles.container, this.props.containerStyle]}>
<View style={styles.leftContainer}>
<Text style={[styles.nameText, styles.textColor]}> {this.props.data.leftText} </Text>
</View>
<View style={styles.rightContainer}>
<Text style={[styles.durationText, styles.textColor]}> {this.props.data.rightTopText + ' hrs'} </Text>
<Text style={[styles.rangeText, styles.textColor]}> {this.props.data.rightBottomText} </Text>
</View>
</View>
</TouchableHighlight>
);
}
If you still hava any problem then let me know :)
In case you want to have it in parent, just fix your code to this:
export default class LabelsView extends Component {
// Make TouchableHighlight wrapper work
setNativeProps = (nativeProps) => {
this._root.setNativeProps(nativeProps);
}
render() {
return (
<View ref={component => this._root = component} style={[styles.container, this.props.containerStyle]}>
<View style={styles.leftContainer}>
<Text style={[styles.nameText, styles.textColor]}> {this.props.data.leftText} </Text>
</View>
<View style={styles.rightContainer}>
<Text style={[styles.durationText, styles.textColor]}> {this.props.data.rightTopText + ' hrs'} </Text>
<Text style={[styles.rangeText, styles.textColor]}> {this.props.data.rightBottomText} </Text>
</View>
</View>
);
}
}
You missed ref={component => this._root = component}
import ViewPager from 'react-native-viewpager';
constructor(props){
super(props);
this._renderPage = this._renderPage.bind(this);
this._renderRowSablon = this._renderRowSablon.bind(this);
this.PageChange = this.PageChange.bind(this);
this.count = 0;
this.state = {
count: 0,
info : this.props.values,
page: 0,
pages:pages,
}
}
PageChange(x){
switch(x){
case 'next':
if( this.state.count< (this.state.info.sayfa - 1) ){
this.viewpager.goToPage(this.state.count + 1);
this.setState({count: this.state.count+ 1});
}
break;
}
}
render(){
<View style={{flex:1}}>
<ViewPager
ref={(viewpager) => {this.viewpager = viewpager}}
dataSource={this.state.dataSource}
renderPage={this._renderPage}
onChangePage={this._pageChange}
isLoop={false}
renderPageIndicator={false}
locked={true}
autoPlay={false}/>
</View>
<View style={{flex:1}}>
<TouchableHighlight onPress={() => { this.PageChange('next'); }}>
<Text>Next</Text>
</TouchableHighlight>
</View>
}
When the setState function is cleared, page change is taking place. When the setState function is added (as above) setState works but the page change (gotoPage) does not work. Does not show error / warning What exactly is the problem?
It seems that you are out of scope. When using arrow functions, the scope of 'this' becomes lexical.
Try changing <TouchableHighlight onPress={() => { this.PageChange('next'); }}>
to <TouchableHighlight onPress={this.PageChange('next')}>
See this question:
React-Native: Cannot access state values or any methods declared from renderRow
' In My listview two component:first,second.
In the second there are map function return third (array elements).
How can I pass and the click event in the first, second, third & inner. For comments & sub comments in post. In the bellow updateComments is not working. And one thing, WallUpdates is also child component.'
class WallUpdates extends Component {
constructor(props) {
super(props);
this.updateComments = this.updateComments.bind(this);
}
updateComments(){
alert("updatecommentPost");
}
render() {
return(
<View>
<Text>
{this.props.allPost}
</Text>
<ListView
dataSource={this.props.dataSource} renderRow={this.renderMovie} style={styles.listView}
/>
</View>
);
}
renderMovie(list) {
var commentNodes = list.comment.map((item, i) => {
console.log(item);
console.log(item.commentcontent);
return (<Comments key={item.cid} comment={item.commentcontent} />);
});
//
return (
<View style={styles.container} >
<Text style={styles.year}>{list.postcontent + '\n\n'}</Text>
<TouchableOpacity onPress={this.updateComments}>
<View style={styles.buttonsmall}>
<Text style={styles.buttonsmallText}>comments!</Text>
</View>
</TouchableOpacity>
{commentNodes}
</View>
);
}
}
I have changed - renderMovie(list) to renderMovie = (list) => and also change onPress={this.updateComments} to onPress={() => { this.updateMe() }} & this is working for me .
I found example code on a facebook React Native page which shows how to use setNativeProp to clear text on a click but I can't see how to do it with multiple text boxes. Here is the code:
var App = React.createClass({
clearText() {
this._textInput.setNativeProps({text: ''});
},
render() {
return (
<View style={styles.container}>
<TextInput ref={component => this._textInput = component}
style={styles.textInput} />
<TouchableOpacity onPress={this.clearText}>
<Text>Clear text</Text>
</TouchableOpacity>
</View>
);
}
});
The ref seems to be fixed in the function so will always target the same TextInput box. How can I alter the function to target any TextInput box I indicate?
This should work. Notice that the ref on the TextInput needs to be the one you call from the clearText functino.
var App = React.createClass({
clearText(fieldName) {
this.refs[fieldName].setNativeProps({text: ''});
},
render() {
return (
<View style={styles.container}>
<TextInput ref={'textInput1'} style={styles.textInput} />
<TouchableOpacity onPress={() => this.clearText('textInput1')}>
<Text>Clear text</Text>
</TouchableOpacity>
<TextInput ref={'textInput2'} style={styles.textInput} />
<TouchableOpacity onPress={() => this.clearText('textInput2')}>
<Text>Clear text</Text>
</TouchableOpacity>
</View>
);
}
});
Updated my answer to clear different fields.
You can also use something like this to clear the text of TextInput.
clearText(fieldName) {
this.refs[fieldName].clear(0);
},