Unable to set state from the response of the api - react-native

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()});

Related

Multiple useEffect in react-native to achieve mentioned functionality

I need help with the async nature of Async storage and axios api. Here's the functionality that I am trying to achieve ->
send request to two separate api to get some data.
display that data on the screen with some additional text
api request are authenticated so a token is passed as Authentication Header
I have attached the current implementation, I am having the a number of errors in this
Errors:
Login_token not set in state after fetching from Async Storage.
Data not set in state after api call
both resulting in either failed api calls or undefined state errors on render
This is my code.
import React, { FunctionComponent, useEffect, useCallback, useState} from 'react';
import { StyleSheet, View} from 'react-native';
// chat
import { GiftedChat } from 'react-native-gifted-chat';
// navigation
import { RootStackParamList } from '../../navigators/RootStack';
import { StackScreenProps } from '#react-navigation/stack';
export type Props = StackScreenProps<RootStackParamList, "Chat">;
// api
import { Convo_details, Send_Msg, List_Msg, Expert_Public_Profile } from '../../api/UserApi';
import Spinner from 'react-native-loading-spinner-overlay';
import AsyncStorage from '#react-native-async-storage/async-storage';
import uuid from 'react-native-uuid';
const Chat: FunctionComponent<Props> = ({ navigation, route, ...props }) => {
// console.log(props.route.params);
const [login_token, setlogin_token] = useState('')
const [conversation_id, setconversation_id] = useState('')
const [conversation_details, setconversation_details] = useState({})
const [currentuser, setcurrentuser] = useState({})
const [loading, setLoading] = useState(false);
const [expertuid, setexpertuid] = useState('')
const [ExpertProfile, setExpertProfile] = useState({})
const [messages, setMessages] = useState([]);
useEffect(() => {
getlogintoken()
console.log("####################################","getlogintoken");
}, [])
/* conversationid */
useEffect(() => {
if (route.params != null) {
setconversation_id(route.params[0])
}
console.log("####################################","conversation id");
}, [])
/* expert uid */
useEffect(() => {
if (route.params != null) {
setexpertuid(route.params[1])
}
console.log("####################################","expert uid");
}, [])
/* expert public profile */
useEffect(() => {
getexpertpublicprofile()
getConvo_details()
console.log("####################################","convo_details");
}, [])
useEffect(() => {
// get current user
AsyncStorage.getItem("currentuser").then(res => {
if (res != null) setcurrentuser(res)
else alert("Current user not found")
})
console.log("####################################","current user");
}, [])
// set welcome msg
useEffect(() => {
if (Object.keys(conversation_details).length != 0 && Object.keys(ExpertProfile).length != 0)
setwelcomemsg()
}, [])
const onSend = useCallback(async (messages = []) => {
// console.log(messages[0].text);
setMessages(previousMessages => GiftedChat.append(previousMessages, messages))
const data = {
conversation_id: "f98d6851-a713-4f58-9118-77a779ff175f",//conversation_id,
message_type: "TEXT",
body: messages[0].text
}
const res: any = await Send_Msg(data, login_token)
.catch(error => {
alert(`Send_Msg -> ${error}`)
console.log(error);
return
})
if (res.status == 200) {
console.log(res.data);
} else console.log(res);
}, [])
const getexpertpublicprofile = async () => {
setLoading(true)
const res: any = await Expert_Public_Profile(expertuid, login_token)
.catch(error => {
setLoading(false)
console.log("Expert public profile ->");
alert(`Expert public profile ->${error.message}`)
console.log(error);
return
})
setLoading(false)
if (res.status === 200) setExpertProfile(res.data)
else {
alert(`get expert public profile${res.data.message}`)
console.log("getexpertpublicprofile -->");
console.log(res.data);
}
}
const getlogintoken = () => {
AsyncStorage.getItem("login_token").then(res => {
if (res != null) {
setLoading(false)
setlogin_token(res)
}
else alert("No login token found")
})
}
const getConvo_details = async () => {
setLoading(true)
const res: any = await Convo_details(conversation_id, login_token)
.catch(error => {
setLoading(false)
alert(`Convo_details-->${error.message}`)
console.log("Convo_details -->");
console.log(error);
return
})
setLoading(false)
if (res.status === 200) setconversation_details(res.data)
else {
alert(`get convo details-> ${res.data.message}`)
console.log("getConvo_details -->");
console.log(res.data);
}
}
const setwelcomemsg = () => {
try {
let user = JSON.parse(currentuser)
let messages = [
{
_id: uuid.v4().toString(),
conversation_id: conversation_details.conversation_id,
created_at: new Date(),
from: conversation_details.recipient.user_uid,
type: "TEXT",
text: `About Me - ${ExpertProfile.bio}`,
user: {
_id: conversation_details.recipient.user_uid,
}
},
{
_id: uuid.v4().toString(),
conversation_id: conversation_details.conversation_id,
created_at: new Date(),
from: conversation_details.recipient.user_uid,
type: "TEXT",
text: `My name is ${conversation_details.recipient.name}`,
user: {
_id: conversation_details.recipient.user_uid,
}
},
{
_id: uuid.v4().toString(),
conversation_id: conversation_details.conversation_id,
created_at: new Date(),
from: conversation_details.recipient.user_uid,
type: "TEXT",
text: `Hi ${user.full_name}`,
user: {
_id: conversation_details.recipient.user_uid,
}
}]
setMessages(previousMessages => GiftedChat.append(previousMessages, messages))
} catch (error) {
console.log("try -> set welcome msg");
console.log(error);
return
}
}
return (
<View style={styles.maincontainer}>
<Spinner
visible={loading}
textContent={'Loading...'}
textStyle={{ color: '#FFF' }}
/>
<GiftedChat
messages={messages}
onSend={messages => onSend(messages)}
user={{
_id: currentuser.user_uid,
}}
isTyping={false}
scrollToBottom={true}
showAvatarForEveryMessage={true}
renderAvatar={() => null}
/>
</View>
);
}
export default Chat;
const styles = StyleSheet.create({
maincontainer: {
flex: 1,
},
});
When axios returns, it usually give the response as res.data, so in your case, try either res.data or res.data.yourToken (I'm not sure how it's your object).
Gurav,
As far as your code above, The api call's will trigger even before you get currentuser or loginToken. You have to handle the api call after getting the currentuser and loginToken. This can be gracefully handled with async, await.
example code:
useEffect(() => {
getData()
}, [])
useEffect(() => {
if(login_token && currentuser) {
//The api call goes here after you get the logintoken andcurrentuser.
// The above condition is just an example but will vary based on your requirements
}
}, [login_token, currentuser])
const getData = async () => {
await getlogintoken()
await getcurrentuser()
}
const getlogintoken = async () => {
await AsyncStorage.getItem("login_token").then(res => {
if (res != null) {
setLoading(false)
setlogin_token(res)
}
else alert("No login token found")
})
}
const getcurrentuser = async () => {
await AsyncStorage.getItem("currentuser").then(res => {
if (res != null) setcurrentuser(res)
else alert("Current user not found")
})
}

React Native RNPicker select

i am using RNPicker Select in which i want dropdown items to get from api but i am getting empty dropdown with no values
following is my code
class Component extends Component {
constructor(props) {
super(props);
this.state = {
inqSourceList: [],
}
componentDidMount() {
this.fetchSource();
}
fetchSource = () => {
getInqSourceList('view=select', this, null)
}
<RNPickerSelect
items={this.state.inqSourceList}
name="source"
value={this.state.source ? this.state.source.id : null}
onValueChange={value => {
this.setState({
source:value,
});
}}
style={{marginBottom: 10}}
/>
and my api code is
export async function getModuleList (moduleName, params,error) {
let token = await AsyncStorage.getItem('token');
axios
.get(BASE_URL + moduleName + "?" + params, {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: "Bearer " + token },
})
.then((res) => {
var bytes = CryptoJS.AES.decrypt(res.data.toString(), ENCDEC);
res.data = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
// success(res.data);
// console.log(res.data)
})
.catch(error);
}
in this i am getting values from backend when i use console.log(res.data) i get list of options..but the dropdown item is empty..
thanks
here is the function where inqSourceList is set
export function getInqSourceList(params, _this, next) {
getModuleList('settings/inquiry-source', params, data => {
_this.setState({inqSourceList: data.rows});
if (next) next(data);
});
}

React Native accessing SecureStore

I have searched Google for a clear answer on this but cant find one. Below is my code.
In a previous screen I have stored the token to SecureStore
I'm now trying to access it from a different screen.
(async () => {
const token = await SecureStore.getItemAsync('token');
return token;
})();
export default class App extends React.Component {
constructor(){
super();
this.state = {
data: [],
loaded: true,
error: null,
token: token
}
}
Can anyone advise me how to get the value from SecureStore to my state inside the class?
Entire Code
import React, { Component} from 'react';
import { Text, Button, ScrollView } from 'react-native';
import { globalStyles } from '../styles/global';
import * as SecureStore from 'expo-secure-store';
(async () => {
const token = await SecureStore.getItemAsync('token');
//console.log('token output 1 ' + token);
return token;
})();
export default class App extends React.Component {
constructor() {
super();
this.state = {
data: [],
loaded: true,
error: null,
token: ""
};
}
baseURL = 'https://www.example.co.uk/api/auth';
getData = (ev)=>{
this.setState({loaded:false, error: null});
let url = this.baseURL + '/list';
let h = new Headers();
console.log('token output 2 = ' + this.state.token);
h.append('Authorization', 'Bearer tokenToBePutHere');
h.append('Content-Type', 'application/json');
h.append('X-Requested-With', 'XMLHttpRequest');
let req = new Request(url, {
headers: h,
method: 'GET'
});
fetch(req)
.then(response=>response.json())
.then(this.showData)
.catch(this.badStuff)
}
showData = (data)=>{
this.setState({loaded:true, data:data});
}
badStuff = (err) => {
this.setState({loaded: true, error: err.message});
}
componentDidMount() {
(async () => {
const token = await SecureStore.getItemAsync('token');
this.setState({ token });
})();
this.getData();
}
render() {
return (
<ScrollView style={globalStyles.container}>
{ !this.state.loaded && (
<Text>LOADING</Text>
)}
<Text>Your Lists Are:</Text>
{/*} <Button title="Get Data"
onPress={this.getData} /> */}
{ this.state.error && (
<Text style={styles.err}>{this.state.error}</Text>
)}
{ this.state.data && this.state.data.length > 0 && (
this.state.data.map( data => (
<Text key={data.id}>
{ data.lists.name }
</Text>
))
)}
</ScrollView>
);
}
}
I have added all of my code for this screen.
token Output 1 works and outputs the correct token
token Output 2 does not work and returns nothing.
I need to use the token in the api call but cant get it to pass there.
You can use the lifecycle method componentDidMount and callbacks like this:
export default class App extends React.Component {
constructor() {
super();
this.state = {
data: [],
loaded: true,
error: null,
token: ""
};
}
componentDidMount() {
SecureStore.getItemAsync("token").then(token => {
this.setState({ token });
});
}
// ...
}
Same solution using async/await:
class App extends React.Component {
constructor() {
super();
this.state = {
data: [],
loaded: true,
error: null,
token: '',
};
}
componentDidMount() {
(async () => {
const token = await SecureStore.getItemAsync('token');
this.setState({ token });
})();
}
// ...
}
Addressing updated question
If you need only need the token for the fetch request you don't even need to store the token in the state. You can use it directly after retrieving it from SecureStore:
componentDidMount() {
(async () => {
const token = await SecureStore.getItemAsync('token');
// Your fetch code
this.setState({loaded:false, error: null});
let url = this.baseURL + '/list';
let h = new Headers();
h.append('Authorization', `Bearer ${token}`);
h.append('Content-Type', 'application/json');
h.append('X-Requested-With', 'XMLHttpRequest');
let req = new Request(url, {
headers: h,
method: 'GET'
});
fetch(req)
.then(response=>response.json())
.then(() => this.setState({loaded:true, data:data}))
.catch(() => this.badStuff())
})();
}

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

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]
})

How to create component in render part from fetched API?

I'm trying to fetch data from api and I used componentDidMount lifecycle for that, But I have a list in my view which need to be created from that API, so I use map function for received data to get all items and show in render part, But when I run my code I get
this.state.matchInfo.map in not a function
Please help me to solve this problem, I knew that componentDidMount will run after first render so I create an empty state first and hoped that after fetching data, component will render again and will show my data. but it keeps getting me the error
here is my code:
constructor(props) {
super(props);
this.state = {
userName: '',
userToken: '',
userID: '',
firstTime: true,
loading: true,
showAlert : false,
alertType : true,
alertMessage : '',
animatedMatchBtn : new Animated.Value(1),
matchInfo: []
};
}
componentDidMount() {
this._bootstrapAsync(true);
}
_bootstrapAsync = async (timeOutStat = null) => {
const userToken = await AsyncStorage.getItem('userToken');
const userName = await AsyncStorage.getItem('userName');
const userID = await AsyncStorage.getItem('userID');
await this.setState({
userName: userName,
userToken: userToken,
userID: userID,
})
if(timeOutStat) {
this.timeOut = setTimeout(() => {
this.setState({
loading: false,
showAlert: true,
alertType: true,
alertMessage: ErrorList.matchMakingError
});
}, 20000)
}
console.log('token', userToken)
await fetch(getInitUrl('getMatchInfo','',this.props.navigation.getParam('matchID', 0)), {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization : `Bearer ${userToken}`
}
}).then(response => Promise.all([response.ok, response.status ,response.json()]))
.then(([responseOk,responseStatus, body]) => { //
this.setState({loading : false});
clearTimeout(this.timeOut);
if (responseOk) {
console.log('match Info', body);
this.setState({
matchInfo : body
})
} else {
console.log(responseStatus, body);
this.setState({
showAlert : true,
alertType : true,
alertMessage : ErrorList[body.tag]
});
}
})
.catch(error => {
console.error(error);
});
};
render() {
//console.log(puzzleSizes)
let rows = this.state.matchInfo.map((item , index)=>{
return
<MatchDetailBox
/>
})
console.log(rows)
<View>
{rows}
</View>
}
Even though this.setState() is asynchronous, it's not promisified hence it wont't work using promise's .then() or its syntactic sugar async/await. So there's no use for await in front of it. But I guess it creates a one tick delay.
Also why do you have await in front of fetch() and also .then() after that. Shouldn't either of them do?
The error this.state.matchInfo.map is not a function would occur only when this.state.matchInfo is not an array but you have initialized it to be one, so at any point of time matchInfo gets modified it must be becoming non-array like an object or something which doesn't have a native .map().
Have you checked response coming from API? I hope this helps.
this.setState({ matchInfo: body });
mapData = () => {
this.state.matchInfo.map((item , index)=>{
return(
<View>
<MatchDetailBox />
<View>
{item}
</View>
</View>
)
})
}
render() {
return {this.mapData()}
}