React Native - state is not saved in object - react-native

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.

Related

React native view fails to refresh after the state variable updated

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

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.
}

React Native filtering API to retrieve specific data

I'm using Zomato API (https://developers.zomato.com/documentation) and am fairly new to getting data from an API, so far I am able to retrieve categories from the API. However what I want to do is pull category data from a specific city. Here is my code:
APIcall.js
import axios from 'axios';
export const apiCall = async(url)=>{
return await axios.request({
baseURL:"https://developers.zomato.com/api/v2.1/categories",
headers: {
'user-key': "a31bd76da32396a27b6906bf0ca707a2",
},
url : url,
method : 'get'
}).then(async(response) => {
return response.data.categories
}).catch(err => console.log(err))
}
Home.js
export default class HomeScreen extends React.Component {
constructor(props){
super(props);
this.state={
data : []
}
}
async componentDidMount(){
this.setState({
data : await apiCall('')
})
console.log(await apiCall('?cities'))//I tried console logging to see what data I can get all I get is [Error: Request failed with status code 404] undefined
}
render() {
return (
<View>
<FlatList
keyExtractor={item => item.id}
data={this.state.data}
renderItem={({ item }) =>
<Card style={styles.container}>
<Text style={{color:'#000',fontWeight:'bold'}}>{item.categories.name} </Text>
</Card>}
/>
</View>
);
}
}
According to Zomato API documentation, in order to pull category data from a specific city you need to pass city_id as Parameter.
import React, { Component } from 'react';
import { FlatList, ActivityIndicator, Text, View } from 'react-native';
import axios from 'axios';
export default class HomeScreen extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
data: []
}
}
async componentDidMount() {
let result;
try {
result = await axios.request({
method: 'GET',
url: "https://developers.zomato.com/api/v2.1/categories?city_id=280",
headers: {
'Content-Type': 'application/json',
'user-key': "a31bd76da32396a27b6906bf0ca707a2",
},
})
} catch (err) {
err => console.log(err)
}
this.setState({
isLoading: false,
data: result.data.categories
})
}
render() {
return (
<View>
{
this.state.isLoading ?
<View style={{ flex: 1, padding: 20 }}>
<ActivityIndicator />
</View> :
<FlatList
keyExtractor={item => item.id}
data={this.state.data}
renderItem={({ item }) =>
<Text>{item.categories.name} </Text>
}
/>
}
</View>
);
}
}
Hope it helps you.

How do I fetch data from api based on search of the user in React Native?

The goal is to allow the user to input a keyword into a search bar, store the search word or phrase into a string and send a post request to to the movie server and display the results in a FlatList format.
I'm not skilled in javascript, but so far I was able to store the search input into a variable and confirmed it by console logging the search but using that variable to render and display the results in confusing
import React, { Component } from "react";
import {
View,
Text,
FlatList,
StyleSheet
} from "react-native";
import { Container, Header,Item,Input, Left, Body, Right, Button, Icon,
Title } from 'native-base';
class Search extends Component {
constructor(props) {
super(props);
this.state = {text: ''};
this.state = {
dataSource: []
}
}
renderItem = ({item}) => {
return (
<Text>{item.title}</Text>
)}
componentDidMount() {
const apikey = "&apikey=thewdb"
const url = "http://www.omdbapi.com/?s="
fetch(url + this.state.text + url)
.then((response) => response.json())
.then((responseJson)=> {
this.setState({
dataSource: responseJson.Search
})
})
.catch((error) => {
console.log(error)
})
}
render() {
return (
<Container>
<Header
searchBar rounded
>
<Item>
<Icon name="ios-search" />
<Input
placeholder="Type here to translate!"
onChangeText={(text) => this.setState({text})}
/>
</Item>
<Button
transparent
onPress={()=> {
{console.log(this.state.text)}
}
}
>
<Text>Search</Text>
</Button>
</Header>
<FlatList
style={{flex: 1, width:300}}
data={this.state.dataSource}
keyExtractor={(item, index) => 'key'+index}
renderItem={this.renderItem}
/>
</Container>
);
}
}
export default Search;
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
}
});
My code is a bit sloppy so please forgive me on that, I'm still new to coding.
The issue is you are fetching data from API on componentDidMount but it will be called only once (when component gets mounted).
So the best way to fix it is
Create a func called fetchData
fetchData(text) {
this.setState({ text });
const apikey = '&apikey=thewdb';
const url = 'http://www.omdbapi.com/?s=';
fetch(url + text + url)
.then(response => response.json())
.then((responseJson) => {
this.setState({
dataSource: responseJson.Search,
});
})
.catch((error) => {
console.log(error);
});
}
In onChangeText, call fetchData
<Input
placeholder="Type here to translate!"
onChangeText={(text) => {
this.fetchData(text);
}}
/>

React Native: ListView.DataSource is not updating

I am trying to update ListView.DataSource after fetching data from server but its not happening. I have two components, one is imported in other.
From base component I am trying to update ListView.DataSource in other component. Here is my code.
index.android.js
import React, { Component } from "react";
import { ListView, View, Button, AppRegistry } from "react-native";
import OtherComponent from "./Components/OtherComponent";
class MyApp extends Component {
constructor(props) {
super(props);
this.state = {
movies: [{ title: "ABCD" }, { title: "EFGH" }]
};
}
getTopics = () => {
fetch("https://facebook.github.io/react-native/movies.json")
.then(response => response.json())
.then(responseText => {
console.log(responseText.movies);
this.setState({
movies: responseText.movies
});
})
.catch(error => {
console.warn(error);
});
};
render() {
return (
<View>
<Button
onPress={this.getTopics}
title="Get Topics"
color="#841584"
accessibilityLabel="Learn more about this purple button"
/>
<OtherComponent movies={this.state.movies} />
</View>
);
}
}
AppRegistry.registerComponent("MyApp", () => MyApp);
OtheComponent.js
import React, { Component } from "react";
import { View, ListView, Text, StyleSheet, Button } from "react-native";
export default class FormativeRevisionList extends Component {
constructor(props) {
super(props);
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2
});
this.state = {
dataSource: ds.cloneWithRows(props.movies)
};
}
render() {
return (
<View>
<ListView
style={styles.listContainer}
dataSource={this.state.dataSource}
renderRow={rowData => (
<View>
<Text style={styles.listItem}>{rowData.title}</Text>
</View>
)}
/>
</View>
);
}
}
const styles = StyleSheet.create({
listContainer: {
paddingTop: 22
},
listItem: {
fontSize: 30,
fontWeight: "bold",
textAlign: "center"
}
});
In your code, you only set your dataSource in contructor of FormativeRevisionList, this mean FormativeRevisionList will only render the given movies when your first render it.
To render new list after you press Get Topics button , you need to set the dataSource again when it receive new props, this can be achieve by setting it in FormativeRevisionList componentWillReceiveProps
componentWillReceiveProps(nextProps) {
if (nextProps.movies !== this.props.movies) {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(nextProps.movies)
})
}
}
You can read more about componentWillReceiveProps from here