Why AsyncStorage getItem is returning null? - react-native

export const USER_KEY = "isLoggedIn";
export const phoneVerified = () => AsyncStorage.setItem(USER_KEY, 1);
export const userInfoVerified = () => AsyncStorage.setItem(USER_KEY, 2);
I have used the above functions to store the value and the below one to get the value.
export const isSignedIn = () => {
return new Promise((resolve, reject) => {
AsyncStorage.getItem(USER_KEY)
.then(res => {
console.log("from isSignedIn : "+res); //res is showing null.
if (res !== null) {
resolve(res);
} else {
resolve(0);
}
})
.catch(err => reject(err));
});
};
Why this always returns null? I was trying async/await but still getting null. I think somehow the data is not storing.

I'm afraid you can only store strings. Please refer to this React Native AsyncStorage storing values other than strings and this https://facebook.github.io/react-native/docs/asyncstorage.html#setitem
Thanks.

As answered by #Vishu Bhardwaj AsyncStorage accepts only string. So you can use JSON.stringify() and JSON.parse() in such cases.

I was stuck with this stupid problem for almost one week, no other way that is suggested in all communities worked for me, but then I found something that is built of react-native which its setState() callback function: https://medium.learnreact.com/setstate-takes-a-callback-1f71ad5d2296.
so the only way that I guarantee that it's the only secure way so far is this that u use the setState() function in your promise and everything that you need to run, put them on a function and call it for the setState() callback function , this is only way you can make sure yourself that you neither get null nor never calling the function . Here I'm going to provide an example of it which this.tokeToServer() is my function which it's used as a callback function.
try {
AsyncStorage.getItem('firebase_token',(err,item) => {
if (item) {
this.setState({
firebase_token: item,
}),this.tokenToServer();
}
});
} catch (error) {
console.log("Error retrieving data" + error);
}

As presented by friend Abdu4, I had the same problem for 4 days and searching for different sites and forums. Attempts with async/await and others, even though you should use these options, the one you completed and really worked was to assign the value through setState by callback
try {
AsyncStorage.getItem('TOKEN_KEY',(err,item) => {
if (item) {
setToken({
Token: item,
});
}
});
} catch (error) {
console.log("Error retrieving data" + error);
}

Related

Referencing JSON object fields after assigning to variable in useEffect()

I'm attempting to retrieve a JSON object from an API call, and then set a useState variable to the response for use in my app. I am able to successfully receive the JSON response, but if I try to reference specific fields I get an error saying
null is not an object (evaluating data.type). I understand that this happens because initially the data variable is simply null, but I'm not sure the best way to go about preventing this and I suspect I'm doing something fundamentally wrong.
Here's the function which queries the API and retrieves the response data:
export function searchFlightOffers(postData) {
return getAmadeusToken().then(data => {
const config = {
headers: {
'Authorization': `Bearer ${data.access_token}`
}
}
return axios.post("https://test.api.amadeus.com/v2/shopping/flight-offers", postData, config).then(data => data);
});
}
Then the React Native function which uses it
export default function FlightScreen() {
const [flightResponse, setFlightResponse] = useState(null);
useEffect(() => {
searchFlightOffers(postData).then(data => setFlightResponse(data.data));
}, [])
console.log("TEST: ", flightResponse.type);
Is there a more efficient way I should be doing this?
EDIT: To add hopefully a more clear description of the issue, here's the JSX I'm trying to use this data in:
return (
<ScrollView style={{marginTop: 220}}>
{
flightResponse != null ? flightResponse.map(data => {
return (
<FlightCard
key={data.id}
data={data}
onPress={() => {}}
/>
)
}) : false
}
</ScrollView>
If I add a conditional which checks to see if the flightResponse data is null and then map the data if not then this works just fine. Removing the conditional and just leaving it like this:
return (
<ScrollView style={{marginTop: 220}}>
{
flightResponse.map(data => {
return (
<FlightCard
key={data.id}
data={data}
onPress={() => {}}
/>
)
})
}
</ScrollView>
Leaves me with this error: null is not an object (evaluating 'flightResponse.map') .
While technically the conditional is a solution to my problem there must be a cleaner way to handle this no?
Update: A solution to this problem is to change
const [flightResponse, setFlightResponse] = useState(null);
to
const [flightResponse, setFlightResponse] = useState([]);
and then I can remove the conditional from the JSX.
My apology that I didn't see the additional info you have put there. Apparently you have resolved this issue on your own by adding the check for null, which, to my knowledge, is the correct usage pattern.
You have to check for null because the code in useEffect is not guaranteed to complete (because it is async) before react native executes the code to render the components. By checking for null, you place a guard on this exact situation (and other situations, such as error during fetching data, data itself is empty, etc.).
Original answer (obsolete)
Try rewrite your searchFlightOffers function in async/await style. This way, it is clearer what object is returned. I suspect your current version does not return the data.
The rewrite can be something like this (untested, use with caution).
export const searchFlightOffers = async (postData) => {
try {
const token = await getAmadeusToken();
} catch (err) {
// handle error during token acquisition
console.log(err);
}
const config = {
headers: {
'Authorization': `Bearer ${token.access_token}`
}
}
try {
const flightOfferData = await axios.post(
"https://test.api.amadeus.com/v2/shopping/flight-offers",
postData,
config,
);
return flightOfferData;
} catch (err) {
// handle error during flight offer acquisition
console.log(err);
}
}

Issue rendering data from firestore document in react native

I created a map for the array of exercises in my database, and then for each exercise, which is a document reference, I'm getting the data from that document reference and setting it to a state. This is resulting in an infinite loop right now.
If I remove the setExerciseData line, the console logs the exercise object's data that I'm expecting to see. I'm really not sure what the correct way to render the name field from this data is.
{workout.exercises.map((exercise) => {
async function getData(exercise) {
getDoc(exercise).then((doc) => {
console.log(doc.data());
setExerciseData(doc.data());
});
}
getData(exercise);
return (
<Text>{exerciseData.name}</Text>
)
})}
You need to use useEffect() and setState() to be able to render your data. Also, Firebase Firestore is Asynchronous in nature, as a general note, no one should be trying to convert an Async operation into a sync operation as this will cause problems. You need to use an Asynchronous function to fetch data from Firestore. See sample code below:
const getExerciseData = async () => {
const docRef = doc(db, "<collection-name>", '<document-id>')
const docSnap = await getDoc(docRef)
if (docSnap.exists()) {
// console.log("Document data:", docSnap.data())
setExerciseData(docSnap.data())
} else {
// doc.data() will be undefined in this case
console.log("No such document!")
}
}
useEffect(() => {
getExerciseData()
}, [])
return (
<Text>{exerciseData.name}</Text>
)
You could also check my answer on this thread for more use-cases.

Simplest way to merge two useState values

I have a react native hook where trips must be updated every time a createdTrips is added to the state:
const [trips, setTrips] = useState([]);
function fetchCreatedTrips() {
try {
API.graphql(graphqlOperation(onCreateTrip)).subscribe({
next: (result) => {
console.log(result);
const updatedTrips = [...trips, result.value.data.onCreateTrip]
setTrips(updatedTrips)
}
})
} catch (err) { console.log(err) }
}
Now, when i first open the screen, it renders all the trips items of the list.
However with the current code, after i create a trip and go back to that screen, it doesn't currently return all the trips + the newly created one, but only one, that is the newly created one. How can i return all the items of the list? Sorry in advance, i'm a beginner.
While setting the new state on your effect you should include the previous values of the trips. You can use the spread operator to do the same.
const createdTrips = [result.value.data.onCreateTrip];
setTrips([...createdTrips, ...trips]);
or better merge the newly created trip in the updateTrips variable and then set it as the state value,
const udpatedTrips = [...trips,result.value.data.onCreateTrip];
setTrips(udpatedTrips);
The problem in your code is you are firing those two methods at the same time and there can be race conditions, so you see random updates on the screen.
Ideally, you need to bring in some consistency in the API calls and the state update. So first fetchTrips()->then->fetchCreatedTrips(). Try the below code wherein I don't update the state immediately in fetchTrips() but rather pass on the results to fetchCreatedTrips() which completes the API call and updates the state together.
const [trips, setTrips] = useState([]);
useEffect(() => {
fetchTrips();
}, [])
async function fetchTrips() {
try {
const tripData = await API.graphql(graphqlOperation(listTrips));
const trips = tripData.data.listTrips.items
fetchCreatedTrips(trips);
} catch (err) { console.log(err) }
}
function fetchCreatedTrips(fetchedTrips) {
try {
API.graphql(graphqlOperation(onCreateTrip)).subscribe({
next: (result) => {
console.log(result);
const updatedTrips = [...fetchedTrips,...updatedTrips];
setTrips(updatedTrips)
}
})
} catch (err) { console.log(err) }
}
PS: Please handle exceptions correctly.

Is it possible to fire a .then() block without a return value from a promise?

Curious issue I'm having dealing with some callback functions. I need to make a series of API calls that all return promises then I'm trying to take that data and map it to an array that exists on the global scope followed by a function to export the new data as a pdf - my issue is that the then() block is firing before the other function finishes and far before the first API call finshes. `
let fireWatson = async () => {
let watsonClassifed = []
let watsonCallIndex = 0;
let returnedArr = []
for (let i = 0; i < watsonData.length; i++) {
let params = {
classifierId: '***********',
collection: watsonData[i]
}
naturalLanguageClassifier.classifyCollection(params,
function (err, response) {
if (err)
console.log('error:', err);
else
console.log("data returned")
console.log(response.result.collection)
watsonClassifed.push(response.result.collection);
console.log(watsonClassifed)
})
}
}
fireWatson(watsonData).then(res =>
console.log("firing stupid callbback after data")
)
I realize this function isnt actually returning anything but is it possible to still make use of a promise without a return value or is this the main issue im hitting? Ideally - i want the then function to wait until the data is back - mapped to the global array and then outputted but this of course depends on proper synchronicity.
output:
[Done] exited with code=0 in 1.526 seconds
[Running] node "index.js"
firing stupid callbback
data returned
all my sweet sweet data
All functions in JavaScript have returns, it's just that they are implicit if you don't say return explicitly
It's always a bit tricky to mix promises with callbacks. Here is a way you can fireWatson without using any utilities.
let fireWatson = async watsonData => Promise.all(watsonData.map(collection => new Promise((resolve, reject) => {
let params = {
classifierId: '***********',
collection: collection,
}
return naturalLanguageClassifier.classifyCollection(params, function(err, response) => {
if (err) {
reject(err)
} else {
resolve(response)
}
})
})))
Of course, you can simplify this tremendously using a utility I created
const { map } = require('rubico')
let fireWatson = map(collection => new Promise((resolve, reject) => {
let params = {
classifierId: '***********',
collection: watsonData[i]
}
return naturalLanguageClassifier.classifyCollection(params, function(err, response) => {
if (err) {
reject(err)
} else {
resolve(response)
}
})
}))
turns out console.log was firing because every .then() block expects a function.
wrong:
fireWatson(watsonData).then(res =>
console.log("firing stupid callbback after data")
)
right:
fireWatson(watsonData).then(()res =>
console.log("firing stupid callbback after data")
)

Unable to return token from AsyncStorage in React Native

I am using React Native's AsyncStorage to store an authorization token, which will be returned when needed to use for new http requests. While I can successfully store it and console log it, I am having some trouble returning the value. I want to call it in another window, in the fashion of
var x= LocalDb.getAcessToken();
console.log(x);
However it won,t work.
LocalDb.getAccessToken();
This on the other hand, works when getAcessToken() has console.log in it
exports.storeToken=function(token){
AsyncStorage.setItem('access_token', token);
}
^^^^This function successfully saves the token
exports.getAccessToken=function(){
AsyncStorage.getItem('access_token')
.then((value) => {
if(value) {
console.log(value);
**//I want to return the value here, to use in another function**
}
})
.done();
}
I can not return the value when I use the return(value) . How can I return a value from the promise?
You need to return the getAccessToken function with the promise call. I would also return the promise with the value... like this.
exports.getAccessToken=function(){
return AsyncStorage.getItem('access_token')
.then((value) => {
if(value) {
console.log(value);
return value;
}
})
}