React native _this.state.data.map is not a function - react-native

In console, I can get this.state.data in render. Everything looks normal. But I get this.state.data.map is not a function error. What am I doing wrong?
I would be very happy if there is someone who can help. Thanks in advance
export default class ProfileScreen extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
hash: '',
};
}
_retrieveData = async () => {
try {
const value = await AsyncStorage.getItem('token');
console.log(value);
this.setState({
hash: value,
});
} catch (error) {
// Error retrieving data
}
};
getInfo = async () => {
try {
const response = await axios.get('http://yakakartim.net/museb/kollektif/loginInfo?hash=' + this.state.hash);
this.setState({
data: response.data.message
})
} catch (e) {
console.log(e)
}
};
componentDidMount() {
this._retrieveData();
this.getInfo()
}
list = () => {
return this.state.data.map(info => {
return (
<View style={{ margin: 10 }}>
<Text>{info.email}</Text>
</View>
);
});
};
render() {
console.log('render',this.state.data)
console.log('render',this.state.hash)
return <View>{this.list()}</View>;
}
}

This is because you are updating the data variable which is initially an array in state but later in getInfo function you have update it like this
this.setState({
data: response.data.message
})
I dont know what is in "message". But if it is not an array, then map function will not work with "data" as it only works with variables which are iterate-able. I mean which are of array data type.

thanks, the incoming data is not an array. I found the solution like this.
this.setState({
data: [response.data.message]
})

Related

Unable to set state from the response of the api

I have a following function in Api.js
const _getCategories = async () => {
var categories = [];
let token = await getJwt();
var config = {
method: 'get',
url: 'myurl',
headers: {
'Authorization': 'Bearer' + ' ' + token
},
data : ''
};
axios(config)
.then(function (response) {
if(response.status == 200)
{
let res = response.data;
// Create a variable having name in the list.
categories = res.data.apps.map(function(item) {
return {
title: item.name,
color: AppStyles.colorSet.analyticsColor,
lightColor: AppStyles.colorSet.analyticsLightColor,
icon: AppStyles.iconSet.analytics
};
});
// console.log('Returning Categories');
console.log(categories);
return categories;
//console.log(data1)
// Handle and fetch the list of apps
}
else
{
// API didn't go through, generate the error functions
}
})
.catch(function (error) {
console.log(error);
});
};
and I am loading it in homscreen.js
class DashboardScreen extends React.Component {
constructor(props) {
super(props);
const { navigation } = props;
navigation.setOptions({
title: 'Dashboard',
headerLeft: () => (
<TouchableOpacity
onPress={() => {
navigation.openDrawer();
}}
>
<Icon
style={AppStyles.styleSet.menuButton}
name="ios-menu"
size={AppStyles.iconSizeSet.normal}
color={AppStyles.colorSet.mainThemeForegroundColor}
/>
</TouchableOpacity>
),
});
this.state = {
categories: [],
};
}
componentDidMount() {
if (!this.state.data) {
Api.getCategories().then(data => console.log("The data is "+data))
.catch(err => { /*...handle the error...*/});
}
}
onPressCategory = item => {
// Handle onpress for the items
};
render() {
//console.log(this.state.categories);
categoryButtonsRow1 = this.state.categories.map((item, index) => {
if (index < 3) {
return (
<CategoryButton
onPress={() => this.onPressCategory(item)}
color={item.color}
lightColor={item.lightColor}
icon={item.icon}
title={item.title}
/>
);
}
});
return (
<ScrollView style={styles.container}>
<View style={styles.row}>{categoryButtonsRow1}</View>
</ScrollView>
);
}
}
But I am getting category as undefined while printing in render().
I even tried to create an async function in the homescreen.js and call the api with await and set the state after the same but still it is coming as undefined.
Any guesses to what I am doing wrong here. Can anyone help with the same. My best guess is that I am not handling the api request properly.
EDIT
I tried Use Async/Await with Axios in React.js but it is still printing undefined to the same.
The reason for getting undefined is the _getCategories is that its not returning anything and you are chaining using .then to get data so the caller has no way to get this data as a callback is not passed.
You can change the to await like this
const _getCategories = async () => {
var categories = [];
let token = await getJwt();
var config = {
method: 'get',
url: 'myurl',
headers: {
Authorization: 'Bearer' + ' ' + token,
},
data: '',
};
const response = await axios(config);
if (response.status == 200) {
let res = response.data;
// Create a variable having name in the list.
categories = res.data.apps.map(function (item) {
return {
title: item.name,
color: AppStyles.colorSet.analyticsColor,
lightColor: AppStyles.colorSet.analyticsLightColor,
icon: AppStyles.iconSet.analytics,
};
});
// console.log('Returning Categories');
console.log(categories);
return categories;
//console.log(data1)
// Handle and fetch the list of apps
} else {
// API didn't go through, generate the error functions
return null;
}
};
And you can set the state in componentDidMount (should be async)
this.setState({categories:await api._getCategories()});

Can't save parsed API data to state in React Native

Using React-Native I'm trying to pull from an API, convert the provided XML to JS using react-native-xml2js and save it to state.
I have the converted output pushed to console successfully but when I try to save to state and have that state outputted on screen I'm getting a reference error stating that result is not defined.
import React, { Component } from 'react'
import { View, Text } from 'react-native'
import { parseString } from 'react-native-xml2js'
class MPList extends Component {
state = {
data: '',
}
componentDidMount = () => {
fetch('http://data.parliament.uk/membersdataplatform/services/mnis/members/query/House=Commons%7CIsEligible=true', {
method: 'GET'
})
.then((response) => response.text())
.then((responseText) => {
parseString(responseText, function (err, result) {
console.log(result);
return result;
})
this.setState({
data : result
})
})
.catch((error) => {
console.log('Error fetching the feed: ', error);
});
}
render() {
return (
<View>
<Text>
{this.state.data}
</Text>
</View>
)
}
}
export default MPList
New to this, any help very much appreciated!
Try setting the state from within the parseString callback function. I've used an arrow function so you don't need to add explicit bindings.
parseString(responseText, (err, result) => {
if(result) {
this.setState({ data: result });
}
});
Your current code:
parseString(responseText, function(err, result) {
// result is defined here
});
// result is undefined here
this.setState({ data: result });

map is not a function in react-native

I want to get some data from api and display data in my app. This is my code,
class AlbumList extends Component {
state = { albums: [] };
async componentWillMount() {
try {
const data = await axios.get(
'https://rallycoding.herokuapp.com/api/music_albums'
);
this.setState({ albums: data });
} catch (err) {
console.error(err.message);
}
}
renderAlbums() {
return this.state.albums.map(album => <Text>{album.title}</Text>);
}
render() {
return (
<View>
{this.renderAlbums()}
</View>
);
}
}
this will give a error this.state.albums.map is not a function..
any way to solve this?
The error "map it not a function" occurs because axios don't return an array.
Axios returns an object with keys like status, data.
const data = await axios.get(
'https://rallycoding.herokuapp.com/api/music_albums'
);
console.log(data);
console.log(data.data); // album data
this.setState({album: data.data});
When using without await:
axios.get('https://rallycoding.herokuapp.com/api/music_albums')
.then(response => {
this.setState({ album: response.data });
})
.catch(error => {
console.log(error);
});
So you must check the object key "data" returned by axios get.

React-redux: Why is the state undefined in my Home component?

I am having troubles with getting the state in my HomeComponent.js . Every time I try to print it, it return "undefined" .
I've tried different ways to call onPress in my Home component (e.g. onPress={this.printState()}, but none work)
This is my HomeComponent.js
//import statements
const mapStateToProps = state => {
return {
jobTitles: state.jobTitles
}
}
const mapDispatchToProps = dispatch => ({
fetchJobTitles: () => dispatch(fetchJobTitles())
});
class Home extends Component {
constructor(props) {
super(props);
this.state = {
jobInputValue: '',
addressInputValue: ''
};
}
componentDidMount() {
this.props.fetchJobTitles();
}
printState = () => {
console.log('State is: ' +
JSON.stringify(this.state.jobTitles));
}
render() {
return (
<ImageBackground style={styles.bkgImage} source={require('../assets/homepage_background.jpg')}>
//JSX goes here
<Button
title="CAUTĂ"
type="outline"
underlayColor={colors.red}
titleStyle={styles.buttonTitleStyle}
color={colors.red}
style={styles.buttonStyle}
onPress={this.printState}
/>
</ImageBackground>
);
}
}
//some styles
export default connect(mapStateToProps, mapDispatchToProps)(Home);
This is my reducer (jobTitles.js):
import * as ActionTypes from '../ActionTypes';
export const jobTitles = (state = { errMess: null,
jobTitles:[]}, action) => {
switch (action.type) {
case ActionTypes.GET_JOB_TITLES:
return {...state, errMess: null, jobTitles: action.payload};
case ActionTypes.JOB_TITLES_FAILED:
return {...state, errMess: action.payload};
default:
return state;
}
};
And this is my Action Creator:
import * as ActionTypes from './ActionTypes';
import { baseUrl } from '../shared/baseUrl';
export const fetchJobTitles = () => (dispatch) => {
return fetch(baseUrl + 'api/jobs/job_keywords')
.then(response => {
if (response.ok) {
return response;
} else {
var error = new Error('Error ' + response.status + ': ' +
response.statusText);
error.response = response;
throw error;
}
},
error => {
var errmess = new Error(error.message);
throw errmess;
})
.then(response => response.json())
.then(jobTitles => dispatch(addJobTitles(jobTitles)))
.catch(error => dispatch(jobTitlesFailed(error.message)));
};
export const jobTitlesFailed = (errmess) => ({
type: ActionTypes.JOB_TITLES_FAILED,
payload: errmess
});
export const addJobTitles = (jobTitles) => ({
type: ActionTypes.GET_JOB_TITLES,
payload: jobTitles
});
This is how the response from the API looks like:
"jobTitles": Object {
"results": Array [
"Engineer",
"Software",
"Software Architect",
"Software Consultant",
"Solution Architect",
"System Architect"
]
}
I expected the console.log() statement from the print() function in the HomeComponent.js to print the JSON response from the API, but instead it returns "undefined". Any ideas why?
Any help will be greatly appreaciated!
In your code :
this.state = {
jobInputValue: '',
addressInputValue: ''
};
What you try to print :
this.state.jobTitles
Of course it's undefined ! Either log this.props.jobTitles or set state jobTitles to print what you want.
You should use this.props.jobTitles
The mapStateToProps puts data from the redux state into the props of the component. this.state only holds the local state of the component. So jobInputValue and addressInputValue in this case. Everything from mapStateToProps and mapDispatchToProps will end up in the props. (As the name of the function indicates)

React Native setState(…) warning with both componentWillMount and componentDidMount

I'm starting with react-native and in my project I got to a point where everything works but there's this warning:
Warning: setState(...): Can only update a mounted or mounting component.
So, I've looked several QA, tried a few solutions(changing the setState() call from componentWillMount and componentDidMount) but... the warning is always there.
Here is part of the code:
REQUEST_URL = 'http://url/users.php';
(...)
module.exports = React.createClass({
getInitialState: function() {
return {
uid: null,
bid: null,
username: null,
}
},
componentDidMount: function() {
this.fetchData();
},
fetchData: function() {
fetch(REQUEST_URL)
.then( (response) => response.json() )
.then( (json) => {
console.log('setState called');
this.setState({
uid: json.users[0].user_id,
bid: json.users[0].building_id,
username: json.users[0].username
});
})
.done();
},
render: function() {
if (!this.state.uid) { //user is not defined
console.log('not rendered');
return <Text>chargement...</Text>
}
// else
console.log('rendered');
var userId = this.state.uid;
var buildingId = this.state.bid;
var username = this.state.username;
return (
<View style={styles.content}>
<Text style={styles.label}>User Id</Text>
<Text>{userId}</Text>
<Text style={styles.label}>Building Id</Text>
<Text>{buildingId}</Text>
<Text style={styles.label}>Username</Text>
<Text>{username}</Text>
</View>
)
},
});
The users.php returns a json content-type.
Any clues?
Thanx.
The problem may be that react re-mounts certain components multiple times in one render (think that has something to do with the representation of initial values, could not find the question here), therefore your state would be set to a component that is not mounted.
If you set your state in a decoupled timeout that can be cleared when the component unmounts, you avoid setting state on a unmounted component.
componentDidMount() {
this.mounted = true;
// this.fetchTimeout = setTimeout(()=>{
this.fetchData();
// });
},
componentWillUnmount() {
// clearTimeouts(this.fetchTimeout);
this.mounted = false;
},
fetchData() {
fetch(REQUEST_URL)
.then( (response) => response.json() )
.then( (json) => {
console.log('setState called');
if (this.mounted === true){
this.setState({
uid: json.users[0].user_id,
bid: json.users[0].building_id,
username: json.users[0].username
});
}
})
.done();
},
I still don't know if we are supposed to use TimerMixins but this way works without those.
(TimerMixins take care of clearing any timeout or interval set in the component)
EDIT: update sample to only call setState of the component is still mounted.
I do not know if there is a better way, but as far as I know until now you can not cancel a fetch request.