How to use async/await to retrieve value from AsyncStorage in react native by using function - react-native

I have created prefsManager.js - Use for storing and retrieve data from AsyncStorage but I have faced a problem like when print log it return always undefined because of it is Async but I want to print the actual value in a log by the call of the function.
import { AsyncStorage } from 'react-native';
import prefskey from '../utils/constants/prefskeys';
const setValue = async (key, value) => {
await AsyncStorage.setItem(key, value);
}
const getValue = async (key) => {
let value = '';
try {
value = await AsyncStorage.getItem(key) || 'none';
} catch (error) {
// Error retrieving data
console.log(error.message);
}
return value;
};
const prefsmanager = {
setValue,
getValue
}
export default prefsmanager;
I have used this in my Home.js when button press I'm calling this method.
_handlePress() {
await prefsManager.setValue(prefskey.username, this.state.username)
console.log("username =>>", await prefsManager.getValue(prefskey.username));
}

You need to use async keyword on your function like this.
import { AsyncStorage } from 'react-native';
import prefskey from '../utils/constants/prefskeys';
const prefsnamager = {
setValue: function (key, value) {
AsyncStorage.setItem(key, value)
},
getValue: async (key) => {
let value = '';
try {
value = await AsyncStorage.getItem(key) || 'none';
} catch (error) {
// Error retrieving data
console.log(error.message);
}
return value;
}
}
export default prefsnamager;
calling function
_handlePress = () => {
prefsManager.setValue(prefskey.username, this.state.username)
console.log("username =>>" , prefsManager.getValue(prefskey.username));
}

Set value in storage
AsyncStorage.setItem('data','Read Data')
Get value from storage
constructor(props) {
super(props)
this.state = {};
let self=this;
//this function is called everytime , when you visit this screen.
this.__didFocusSubscription = this.props.navigation.addListener('didFocus',payload => {
AsyncStorage.getItem('data').then((value)=>{
if(value==null){
self.setState({count:'no data found'})
}
else{
self.setState({count:value})
}
})
});
}

Actually, it is like localStorage in the web but with a little difference. in getting the item it acts asynchronously. pay attention to below:
AsyncStorage.setItem('key', value);
But in getting it is like below:
AsyncStorage.getItem('key')
.then( value => console.log(value) );

Related

AsyncStorage doesn't return null at first start

I'm making a login using a JWT and deviceStorage, it's working okay but every time I start the app, and with JWT removed from deviceStorage, it start as if already logged in. The problem is that the get method in deviceStorage returns a promise, so I need to either make the get method return null if empty or have my program know if it's a string or a promise
APP.JS
import 'react-native-gesture-handler';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import React, { useState, useEffect } from 'react';
import Login from './src/Pages/Login';
import LoggedIn from './src/Pages/LoggedIn';
import deviceStorage from './src/Service/deviceStorage';
const App = () => {
const [JWT, setJWT] = useState(null);
useEffect(() => {
const checkJWT = () => {
setJWT(deviceStorage.getValue())
}
checkJWT()
}, []
);
const checkJWT = () =>{
if (!JWT || JWT === null || JWT === "") {
return <Login setJWT={setJWT} />
}else if (JWT){
return <LoggedIn JWT={JWT} setJWT={setJWT} />
}
}
return (
<SafeAreaProvider>
{checkJWT()}
</SafeAreaProvider>
)
}
export default App
DEVICESTORAGE
import AsyncStorage from '#react-native-async-storage/async-storage';
const key = 'currentUser';
const deviceStorage = {
async saveItem(value) {
try {
const jsonValue = JSON.stringify(value)
await AsyncStorage.setItem(key, jsonValue);
} catch (error) {
console.log('AsyncStorage Error: ' + error.message);
}
console.log('Done saving Value.')
},
async getValue() {
try {
return await AsyncStorage.getItem(key)
} catch(e) {
console.log('AsyncStorage Error: ' + e.message);
}
console.log('Done getting value.')
},
async removeValue() {
try {
await AsyncStorage.removeItem(key)
} catch(e) {
console.log('AsyncStorage Error: ' + e.message);
}
console.log('Done removing Value.')
}
};
export default deviceStorage;
I hope someone can help me with this
As you pointed out, the get method of deviceStorage returns a promise currently. I think you can do one of these:
If useEffect can accept an async method I think this should work:
useEffect(async () => {
const checkJWT = () => {
setJWT(await deviceStorage.getValue())
}
checkJWT()
}, []);
If not, then something like this should work:
useEffect(() => {
const checkJWT = () => {
deviceStorage.getValue().then(value => setJWT(value));
}
checkJWT()
}, []);
I haven't checked that anywhere if that works, but something along those lines should do the trick. You should make sure that you indeed put a JWT in your setJWT method and not a Promise. Or maybe you could change your setJWT method so it knows, that if it received a promise then you have to wait for the result.
Try out this function :
async getValue() {
try {
let value = null;
value = await AsyncStorage.getItem(key);
return value;
} catch(e) {
console.log('AsyncStorage Error: ' + e.message);
}
console.log('Done getting value.')
}

React-Native Async Storage returns weird object with correct value in two consecutive lines

I've researched this and not found a similar case answered, at my whits end.
I have the following code to get an item from AsyncStorage, which returns the correct value when I log it on the console, but the function's return value is: {\"_40\":0,\"_65\":1,\"_55\":\"Nick\",\"_72\":null}
const getData = async () => {
try {
const value = await AsyncStorage.getItem('userDetails');
if(value !== null) {
const wsi_user = await JSON.parse(value);
console.log("userName: " + wsi_user.userName); // returns "Nick"
return wsi_user.userName; // returns {\"_40\":0,\"_65\":1,\"_55\":\"Nick\",\"_72\":null}
}
} catch(e) {
letter = "D";
}
}
I've seen similar articles where people mention that the promise needs to resolve and I get that, but my result is within the outputted weird object, AND, my result is correct when logged to console the line before return.
Please advise. No clue how to fix this.
You can call this function like:
const result = await getData()
Also, you don't need the await before JSON.parse() as it is synchronous.
Maybe you can use react-native-easy-app, through which you can access AsyncStorage synchronously, and can also store and retrieve objects, strings or Boolean data
import { XStorage } from 'react-native-easy-app';
import { AsyncStorage } from 'react-native';
// or import AsyncStorage from '#react-native-community/async-storage';
export const RNStorage = { userInfo: undefined };
const initCallback = () => {
// From now on, you can write or read the variables in RNStorage synchronously
// equal to [ await AsyncStorage.setItem('userInfo',JSON.stringify({ name:'rufeng', age:30})) ]
RNStorage.userInfo = {name: 'rufeng', age: 30};
};
XStorage.initStorage(RNStorage, AsyncStorage, initCallback);
You need to convert the object to a string before saving it in the async storage AsyncStorage.setItem(key, JSON.stringify(data));
Example:
export const setStorageItem = async (key: string, data: any): Promise<void> => {
return AsyncStorage.setItem(key, JSON.stringify(data));
};
export const getStorageItem = async <T>(key: string): Promise<T | null> => {
const item = await AsyncStorage.getItem(key);
if (item) {
return JSON.parse(item);
}
return null;
};
Example from the docs

AsyncStorage functions just producing garbage in react native

So I implemented this code in a file from the react native docs.
class Storage {
//store data in 'key'
store = async (key, data) => {
try {
await AsyncStorage.setItem(key, data);
} catch (error) {
// Error saving data
console.log(error.message);
}
};
retrieve = async (key) => {
try {
const value = await AsyncStorage.getItem(key);
if (value !== null) {
// We have data!!
console.log(value);
}
} catch (error) {
// Error retrieving data
console.log(error.message);
}
};
}
And this in other I want to use to actually store and retrieve the variables:
strg.store('test', 'testing');
testing = strg.retrieve('test');
I kept getting an error but then looking it up here I figured out my storage output was an object and not a string as I expected. So I used JSON.stringify(***) and this gibberish came out instead of a "testing".
{"_40":0, "_65":0, "_55":null,"_72":null}
edit: I figure out how to use the console to debug and I found out the 'testing' was inside the promise object that comes out of my function. I read a little about async functions and now I want to know how do I extract the values from the promises?
This happened because you are using AsyncStorage - an asynchronous storage system. You have to wait until it done retrieve data from storage to get the proper data.
I think there are two correct ways to get data from your implementation:
Use async with your container function name & await with your function called
async function getData() {
....
let data = await strg.retrieve('test');
console.log("data", data);
}
or simple use .then():
strg.retrieve('test').then((data) => {
console.log("data", data);
// Handle retrieved data
});
Hope that help. :)
This is how i did it and it works like a charm
import { AsyncStorage } from 'react-native';
module.exports = {
retrieve: async (value) => {
try {
let data = await AsyncStorage.getItem(value);
return data;
} catch (err) {
return err;
}
},
store: async (key, value) => {
try {
// stringify the value since value can only be string.
if (typeof (value) === 'object')
value = JSON.stringify(value)
return await AsyncStorage.setItem(key, value);
} catch (err) {
console.log(err)
return err;
}
}
}
Your store and retrieve functions are asyn so you have to use await until the actual task is complete.
So the code should be like below.
await strg.store('test', 'testing');
const testing = await strg.retrieve('test');
The garbage value is a promise so it will be something like the object you got.
If you return value like this you will retrieve it from outside.
const value = await AsyncStorage.getItem(key);
if (value !== null) {
// We have data!!
console.log(value);
return value;
}

Failed in retrieve data from AsyncStorage

I am a beginner at react-native.
I trying to retrieve data that stored from screen1.js in Screen2.js but I failed.
I have import Asyncstorage from react-native for both .js
This how I store variable from screenone.js :
class screenone extends Component {
state = {
oldpin: '000000',
newpin: '',
secpin: '',
};
onPressButton = () => {
if (this.state.newpin == this.state.secpin) {
this.setState(
{ oldpin: this.state.newpin },
async() => await this.storeData());
}
else {
ToastAndroid.show("Password Unmatched", ToastAndroid.SHORT);
}
}
storeData = async () =>{
const {oldpin} = this.state;
let pin : oldpin;
try{
await AsyncStorage.setItem('mypin',pin);
ToastAndroid.show("Password Changed", ToastAndroid.SHORT);
}
catch (err){
console.warn(err)
}}
....
This is how I trying to retrieve data in screentwo.js:
class screentwo extends Component {
constructor(props) {
super(props);
this.onComplete = this.onComplete.bind(this);
this.state = {
pin: ''
};
}
retrieveData = async (mypin) => {
try {
let value = await AsyncStorage.getItem(mypin);
if (value !== null) {
console.log(value);
this.setState({
pin: value})
}
} catch (error) {
console.warn(err)
}
}
onComplete(inputtedPin, clear) {
retrieveData();
if (inputtedPin !== this.state.pin) {
ToastAndroid.show("Incorrect Pin", ToastAndroid.SHORT);
clear();
} else {
ToastAndroid.show("Pin is correct", ToastAndroid.SHORT);
clear();
this.props.navigation.navigate("Dashboard");
}}
....
Error:
Reference Error: ReferenceError:Can't find variable:retrieveData
Am I using the right way to stored and retrieve data?
Any suggestion?
Thank you.
There are a couple of issues that I can see with your code.
Firstly the retrieveData() function. It is asynchronous and should be called with await also you are getting the error: Reference Error: ReferenceError:Can't find variable:retrieveData because you haven't used this
So ideally you should call it await this.retrieveData();
There are a few more issues with this function. You use the parameter mypin but don't seem to pass any parameter to the function when you call it. Fixing this issue you should call retreiveData() like this:
await this.retrieveData('mypin');
Or you could remove passing the paramater altogether, which I will show how to do in my refactor below.
Finally you call retreiveData every time you check the inputtedPin this isn't that efficient, it is asynchronous so it may take some time, and secondly it also takes time for the setState function to complete, which means that the state may not have updated in time when you go to check it against the inputtedPin, meaning that you are checking the inputtedPin against the wrong value.
Code Refactor
This is how I would refactor your component.
Refactor retrieveData so that it no longer takes a parameter and the key is hardcoded in the .getItem
In the componentDidMount get the value of the pin from AsyncStorage and save it to state.
Remove the retrieveData call from onComplete
Here is the refactor
retrieveData = async () => { // parameter have been removed
try {
let value = await AsyncStorage.getItem('mypin'); // notice I am now passing the key as a string not as a parameter
if (value !== null) {
console.log(value);
this.setState({ pin: value })
}
} catch (error) {
console.warn(err)
}
}
// here we call the refactored retrievedData which will set the state.
async componentDidMount () {
await this.retrieveData();
}
onComplete(inputtedPin, clear) {
// we remove the call to retrieveData as we have already gotten the pin in the componentDidMount
if (inputtedPin !== this.state.pin) {
ToastAndroid.show("Incorrect Pin", ToastAndroid.SHORT);
clear();
} else {
ToastAndroid.show("Pin is correct", ToastAndroid.SHORT);
clear();
this.props.navigation.navigate("Dashboard");
}
}
only replace
retrieveData();
to
this.retrieveData();
When you call async method from a caller method that method also become async Try prefix
async onComplete () { await this.retrieveData() }

How to stored a variable in asyncstorage?

I currently learning react-native.
I am trying to stored a variable into asyncstrorage in scriptone.js and calling it in scripttwo.js
But i failed to stored the variable in scriptone.js
What i have import in scriptone.js:
import React, { Component, BackAndroid } from "react";
import { AsyncStorage } from 'AsyncStorage';
import { View, Text, StyleSheet, Button, Image, TouchableOpacity, TextInput, Alert} from "react-native";
This is part of my code in scriptone.js
class SettingScreen extends Component {
state = {
a: '70',
b: '',
c: '',
};
onPressButton = () => {
if (this.state.a == this.state.aa) {
this.setState({ b: this.state.a });
this.storeData();
}
else {
Alert("Try Again");
}
}
storeData(){
const {a} = this.state;
let mynum : a;
AsyncStorage.setItem('array',mynum)
Alert("Saved");
}
...
The error display :
"undefined is not an object(evaluating '_AsyncStorage.AsyncStorage.setItem')
May I know what the problem?
Thank you.
AsyncStorage
Usually to use AsyncStorage you first import it at the top of you file, the documentation says that you should import it as follows:
import { AsyncStorage } from 'react-native';
Which you can see here https://facebook.github.io/react-native/docs/asyncstorage
Obviously you should remove the previous import statement
import { AsyncStorage } from 'AsyncStorage';
as leaving it in will cause name conflicts.
Saving to AsyncStorage
Saving to AsyncStorage is an asynchronous task so you should use an async/await function that means you should update your storeData() function. You can see the documentation https://facebook.github.io/react-native/docs/asyncstorage for how you should do this.
storeData = async () => {
const {a} = this.state;
let mynum = a;
try {
await AsyncStorage.setItem('array', mynum)
Alert("Saved");
} catch (err) {
console.warn(err);
}
}
Setting state
Next it looks like you could be getting yourself into a race condition when you're setting the state. It takes time for setState to set the item to state. So when you call
this.setState({ b: this.state.a });
the state may not have actually been set by the time you call
this.storeData();
leading to the wrong value being stored in AsyncStorage.
To over come this there is a couple of ways you could handle this
Use setState with a callback
Pass the variable to store as a parameter to this.storeData()
Use setState with a callback
This article goes into quite some detail about using setState with a callback https://medium.learnreact.com/setstate-takes-a-callback-1f71ad5d2296 however you could refactor your onPressButton to something like this
onPressButton = () => {
if (this.state.a == this.state.aa) {
this.setState({ b: this.state.a }, async () => { await this.storeData(); });
} else {
Alert("Try Again");
}
}
This will guarantee that this.storeData() won't be run until the state has been updated.
Pass the variable to store as a parameter
This requires refactoring the storeData() function to take a parameter
storeData = async (mynum) => {
try {
await AsyncStorage.setItem('array',mynum)
Alert("Saved");
} catch (err) {
console.warn(err);
}
}
Now to use this function we have to update your onPressButton, Notice that we pass the value that we want to store to storeData that means we no longer have to access it from state inside storeData
onPressButton = async () => {
if (this.state.a == this.state.aa) {
this.setState({ b: this.state.a });
await this.storeData(this.state.a);
} else {
Alert("Try Again");
}
}
Retrieving from AsyncStorage
This is also an asynchronous task and requires an async/await. To get the string that you stored all you have to do is pass the correct key to the retrieveData function
retrieveData = async (key) => {
try {
const value = await AsyncStorage.getItem(key);
if (value !== null) {
// We have data!!
console.log(value);
// do something with the value
}
} catch (error) {
// Error retrieving data
}
}