React native view fails to refresh after the state variable updated - react-native

I am new to React and have difficulties on refresh data in the . I am expecting that the view should shows
"Hello World! Shyam"
But it only shows "Hellow World".
My code:
import React, { Component } from 'react';
import { Text, View } from 'react-native';
export default class WhatsDes extends Component {
constructor(props) {
super(props);
this.state = {name:'', email:''};
}
render() {
console.log('Start render ....');
const url = 'http://192.168.1.13:8091/employees';
fetch(url)
.then(response => response.json())
.then(responseJson => {
console.log('ok 1: '+ JSON.stringify(responseJson));
console.log('ok 2: '+responseJson[0].name);
this.state.name = responseJson[0].name;
})
.catch(error => {
console.log('error' +error);
});
console.log('Show view ...' );
console.log('this.state.name ...' + this.state.name);
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>Hello, world ! {this.state.name}</Text>
</View>
);
}
}
Log output:
LOG Running "WhatsDes" with {"rootTag":201}
LOG Start render ....
LOG Show view ...
LOG this.state.name ...
LOG ok 1: [{"name":"Shyam","email":"shyamjaiswal#gmail.com"},{"name":"Bob","email":"bob32#gmail.com"},{"name":"Jai","email":"jai87#gmail.com"}]
LOG ok 2: Shyam

Do not mutate state. If you want to update state, use the setState method.
change your state update,
this.state.name = responseJson[0].name;
to
this.setState({name: responseJson[0].name});
Read more about setState at https://reactjs.org/docs/react-component.html#setstate
Edit: Upon close inspection, noticed a few more no no in your code.
You are doing all these actions inside of the render function. This is not the correct way of doing it.
Move your API calls to componentDidMount function, which will only be executed on your component mount. Doing it in render as you are doing now will repeatedly call that function on each render and will throw error Max callstack exceeded.
Change your code to,
import React, { Component } from 'react';
import { Text, View } from 'react-native';
export default class WhatsDes extends Component {
constructor(props) {
super(props);
this.state = {name:'', email:''};
}
componentDidMount() {
const url = 'http://192.168.1.13:8091/employees';
fetch(url)
.then(response => response.json())
.then(responseJson => {
this.setState({ name: responseJson[0].name });
})
.catch(error => {
console.log('error' +error);
});
}
render() {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>Hello, world ! {this.state.name}</Text>
</View>
);
}
}
perhaps a good idea to look through the Life cycle events - React

Related

How to separate axios call from component in react native?

I am new to react native. I have following component in my project for now I have written for fetching API in same component but want to separate it out. I am getting difficulty for how can i access variable which I am using in "getAlbum" method from outside of component.
Is there standard way to separate API call from component?
import React, { Component } from 'react';
import {
FlatList, Text, View, Image, TouchableOpacity,
} from 'react-native';
import { ActivityIndicator, Provider } from 'react-native-paper';
import axios from 'axios';
import styles from '../style/ThumbnailView.component.style';
import ErrorAlert from '../common/ErrorAlert';
import * as myConstant from '../common/Constants';
export default class HomeScreen extends Component {
// For to Navigation header
static navigationOptions = () => ({
headerTitle: 'Album Information',
});
constructor(props) {
super(props);
this.state = {
isLoading: true,
apiLoadingError: false,
};
}
getAlbums() {
const { navigation } = this.props;
const albumId = navigation.getParam('albumID', 'no data');
axios
.get(
myConstant.API + `photos?albumId=${albumId}`, {timeout: myConstant.TIMEOUT}
)
.then((response) => {
this.setState({
isLoading: false,
dataSource: response.data,
});
})
.catch(err => {
this.setState({isLoading: false, apiLoadingError: true})
});
}
componentDidMount() {
this.getAlbums();
}
render() {
if (this.state.isLoading) {
return (
<View style={{ flex: 1, paddingTop: 30 }}>
<ActivityIndicator animating={true} size='large' />
</View>
);
}
if (this.state.apiLoadingError) {
return (
<ErrorAlert />
);
}
return (
<React.Fragment>
<Provider>
<View style={styles.listContainer} >
<FlatList
testID='flatlist'
data={ this.state.dataSource } numColumns={3}
renderItem={({ item }) => <View style={styles.listRowContainer}>
<TouchableOpacity onPress={() => this.props.navigation.navigate('AlbumDetailsViewScreen', {
albumTitle: item.title, albumImg: item.url
})} style={styles.listRow}>
<View style={styles.listTextNavVIew}>
<Image source = {{ uri: item.thumbnailUrl }} style={styles.imageViewContainer} />
</View>
</TouchableOpacity>
</View>
}
keyExtractor = { (item, index) => index.toString() }
/>
</View>
</Provider>
</React.Fragment>
);
}
}
You can separate your axios call by making another class with function which will receive 'albumID' as an argument - then add it to your axios link. If you want to call this function from another class just make it static and use like in example below. Then you can map your fetchData to parse it into state. Hope it will help you.
export class Api {
static fetchData = (albumId: string) => {
//here your axios call which will return an array
}
}
export default class HomeScreen extends React.Component {
state = {
//.....
}
receivedData = Api.fetchData('albumID');
//you can map array here to get what you want.
}

How i can get the route param in react native

How i can get the route param in react native.
onPress={() => props.navigation.navigate('SingleTour', { item } )}
This is the way you can send the item eg index.js
<TouchableOpacity onPress={() => props.navigation.navigate('SingleTour', { item } )}>
<Text>SingleTour</Text>
</TouchableOpacity>
in SingleTour.js you can get this item like this
const { item } = this.props.route.params;
I assume that you need to pass param with navigation and set param to the new screen's state.
Assuming that this is your onPress event on base screen
onPress={() => props.navigation.navigate('SingleTour', { item } )}
So this is how you set param in new screen's state
SingleTour.js
import React, { Component } from 'react';
import {
View,
StyleSheet
} from 'react-native';
export default class SingleTour extends Component {
constructor(props) {
super(props);
this.state = {
item : this.props.navigation.state.params.item,
}
}
render() {
return (
<View style={styles.container}>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
}
});
Hope you have clear about this :)
This is your solution.
onPress={() => {
/* 1. Navigate to the Details route with params */
navigation.navigate('Details', {
itemId: 86,
otherParam: 'anything you want here',
});
}}
If any problem occurs, you can refer to this Official Documentation
Refer this Expo Snack Online Demo

setState in an interval consuming huge memory

I tried create a countdown timer and found that setState in an interval consuming a lot of memory and it will grow until it crash.
I tried creating a fresh new react-native app using react-native init intervaltest
then I got this in App.js
import React, { Component } from "react";
import { StyleSheet, Text, View } from "react-native";
type Props = {};
export default class App extends Component<Props> {
state = {
countdownNumber: 10000000
};
componentDidMount = () => {
if (!this.interval) {
this.interval = setInterval(() => {
this.setState(prevState => ({
countdownNumber: prevState.countdownNumber - 1
}));
}, 10);
}
};
render() {
const { countdownNumber } = this.state;
return (
<View style={styles.container}>
<Text style={styles.welcome}>{countdownNumber}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#F5FCFF"
},
welcome: {
fontSize: 20,
textAlign: "center",
margin: 10
},
});
It is a very simple code anyway. Is there any way to solve this memory thirsty behavior of react-native?
In your code setInterval is called in componentDidMount and componetDidMount will be called once in whole component life-cycle. So, the function within setInterval will be called once only. i.e. just after the first render but upon successive render, the componentDidMount won't be called.
Simple solution is:
export default class App extends Component<Props> {
state = {
countdownNumber: 10000000
};
componentDidMount = () => {
if (!this.interval) {
this.interval = setInterval(() => {
this.setState(prevState => ({
countdownNumber: prevState.countdownNumber - 1
}));
}, 10);
}
};
componentDidUpdate(){
if(this.state.countdownNumber === 1){
clearInterval(this.interval);
}
}
componentWillUnmount(){
clearInterval(this.interval);
}
render() {
const { countdownNumber } = this.state;
return (
<View style={styles.container}>
<Text style={styles.welcome}>{countdownNumber}</Text>
</View>
);
}
}
Memory leak due to setInterval::If we unmount the component before calling clearInterval, there is a memory leak because the interval that is set in componentDidMount starts timer and the timer is not stopped when component unmount's. React provides the componentWillUnmount life-cycle method as an opportunity to clear anything that needs to be cleared when the component is unmounted / removed.

React Native - state is not saved in object

Im trying out React Native an now im fetching a weather forecast from openweather API. the data is getting fetched after the user type in the city an click the button.
The problem is that i am trying to save the response to the state objects property "forecast" but its not beeing saved.
What am i doing wrong?
import React, {Component} from 'react';
import {StyleSheet, Text ,TextInput, View, Button} from 'react-native';
export default class App extends Component {
constructor(props){
super(props);
this.state = {
text:"",
forecast:null,
hasData: false
}
}
userTextChange = (input) => {
this.setState({
text:input
})
}
getMovies = () => {
var url = 'https://api.openweathermap.org/data/2.5/weather?q='+this.state.text+'&units=metric&appid=7d6b48897fecf4839e128d90c0fa1288';
fetch(url)
.then((response) => response.json())
.then((response) => {
this.setState = ({
forecast:response,
hasData:true
})
console.log(response) <-- This is a json reponse with one object
})
.catch((error) => {
console.log("Error: ",error);
});
}
render() {
return (
<View style={styles.container} >
<TextInput
style={{width:'80%',borderRadius:8,marginTop:70,height:60,backgroundColor:'#f1f1f1',textAlign:'center',borderWidth:1,borderColor:'#ccc'}}
placeholder=""
onChangeText={this.userTextChange}
/>
<Button
title="Get forecats"
style={{backgroundColor:'#000',height:50,width:'50%',marginTop:30,marginBottom:30}}
onPress={()=>this.getMovies()}
/>
<View style={{width:'90%',height:'68%', backgroundColor:'rgba(0,0,0,0.5)',alignItems:'center',paddingTop:20}}>
<Text style={{color:'#000',fontSize:22}}>{this.state.forecast.name}</Text> <-- THIS IS NULL
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex:1,
alignItems:'center'
},
});
Herer is the JSON response frpm openweather API
The following line:
this.setState = ({
forecast:response,
hasData:true
})
should be:
this.setState({
forecast:response,
hasData:true
})
You should also consider initializing forecast in state to an empty object.

react-native componentWillUnmount not working while navigating

I am doing this simple steps but unmount was not calling I don't know why. Please I need a solution for this I need unmount to be get called while navigating to another screen...
class Homemain extends Component {
constructor(props) {
super(props);
}
componentWillMount(){
alert('willMount')
}
componentDidMount(){
alert('didMount')
}
componentWillUnmount(){
alert('unMount')
}
Details = () => {
this.props.navigation.navigate('routedetailsheader')
}
render() {
return(
<View style={styles.container}>
<TouchableOpacity onPress={() => this.Details()} style={{ flex: .45, justifyContent: 'center', alignItems: 'center', marginTop: '10%', marginRight: '10%' }}>
<Image
source={require('./../Asset/Images/child-notification.png')}
style={{ flex: 1, height: height / 100 * 20, width: width / 100 * 20, resizeMode: 'contain' }} />
<Text
style={{ flex: 0.5, justifyContent: 'center', fontSize: width / 100 * 4, fontStyle: 'italic', fontWeight: '400', color: '#000', paddingTop: 10 }}>Details</Text>
</TouchableOpacity>
</View>
);
}
}
export default (Homemain);
This is my RouteConfiguration in this way I am navigating to the next screen. Can someone please help me for this error so that i can proceed to the next steps
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { addNavigationHelpers, NavigationActions } from 'react-navigation';
import { connect } from 'react-redux';
import { BackHandler } from 'react-native';
import { Stack } from './navigationConfiguration';
const getCurrentScreen = (navigationState) => {
if (!navigationState) {
return null
}
const route = navigationState.routes[navigationState.index]
if (route.routes) {
return getCurrentScreen(route)
}
return route.routeName
}
class StackNavigation extends Component {
static propTypes = {
dispatch: PropTypes.func.isRequired,
navigation: PropTypes.shape().isRequired,
};
constructor(props) {
super(props);
BackHandler.addEventListener('hardwareBackPress', this.backAction);
}
//backAction = () => this.navigator.props.navigation.goBack();
backAction = () => {
const { dispatch, navigation } = this.props;
const currentScreen = getCurrentScreen(navigation)
if (currentScreen === 'Homemain') {
return false
}
else
if (currentScreen === 'Login') {
return false
}
dispatch(NavigationActions.back());
return true;
};
render() {
const { dispatch, navigation } = this.props;
return (
<Stack
ref={(ref) => { this.navigator = ref; }}
navigation={
addNavigationHelpers({
dispatch,
state: navigation,
})
}
/>
);
}
}
export default connect(state => ({ navigation: state.stack }))(StackNavigation);
Routing to a new screen does not unmount the current screen.
For you usecase you instead of writing the code in componentWillUnmount you can continue by writing it after calling navigate in Details itself.
If you are looking for a callback when you press back from the new screen to come back to the current screen. Use goBack as shown in https://github.com/react-navigation/react-navigation/issues/733
If you are using a stack navigator, then routing to a new view loads the new view above the old one. The old view is still there for when you navigate back.
As I understand from your question and code you are using redux with navigation and want to unmount a screen. So what I did I just added a screen component inside another component to make my screen component as child.
e.g. below is the snippet that I am using to unmount the PushScreen from PushedData component.
I render PushScreen and inside it there is component PushedData that originally making the view. On PushedData `componentWillMount I am just doing some conditional functionality and on success I am just unmounting PushData from PushScreen.
class PushScreen extends Component{
state ={ controllerLaunched: false };
updateControllerLauncher = () => {
this.setState({ controllerLaunched: true });
}
render (){
if(this.state.controllerLaunched){
return null;
} else {
return <PushedData handleControllerLauncher={this.updateControllerLauncher} />;
}
}
}
class PushedData extends Component{
componentWillMount(){
this.unmountPushData();//calling this method after some conditions.
}
unmountPushData = () => {
this.props.handleControllerLauncher();
}
render(){
return (
<View><Text>Component mounted</Text></View>
);
}
}
Let me know if you need more information.
When you use Stack Navigator then routing to a new view loads the new view above the old one as Rob Walker said. There is a workaround. You can bind blur event listener on componentDidMount using navigation prop:
componentDidMount() {
this.props.navigation.addListener('blur', () => {
alert('screen changed');
})
}
So when your screen goes out of focus the event listener is called.
You can find more about events right here.
If you want to go next Screen use (.replace instead .navigate) where you want to call componentWillUnmount. and if you want to go back to one of previous screens use .pop or .popToTop.
you can set condition for costume function that u write for hardware button , for example when ( for example for React Native Router Flux ) Actions.currentScene === 'Home' do something or other conditions u want .