I have small React Native Component in which I try to initialize state by reading values from local storage (AsyncStorage). But when I call the outside method from constructor I get undefined as state value.
Here is part of my code:
constructor(props) {
super(props);
this.state = {
languageChecked: I18n.locale,
anonymityChecked: this.pickLocalKey('anonymity', true),
locationChecked: this.pickLocalKey('location', false),
notificationChecked: this.pickLocalKey('notification', false),
statisticsChecked: this.pickLocalKey('statistics', false),
};
console.log("STATE: " + this.state.anonymityChecked); // UNDEFINED
}
pickLocalKey(key, defaultValue) {
AsyncStorage.getItem(key).then((value) => {
const item = (value !== null && value !== undefined) ? value : defaultValue;
console.log(item);
return item;
});
}
Does anyone knows how to solve this issue? Thank you in advance
There are several issues with the code that you pasted in your question.
Firstly the constructor
is synchronous and you trying to perform asynchronous tasks inside it. This is not a good idea.
If you need to load values from AsyncStorage
and store them in state
you should do it in the componentDidMount
So set initial values in the constructor, adding an additional value loaded: false
. This will become important later on.
constructor (props) {
super(props);
this.state = {
languageChecked: I18n.locale,
anonymityChecked: true,
locationChecked: false,
notificationChecked: false,
statisticsChecked: false,
loaded: false // <- add this additional value
};
}
Currently your pickLocalKey
function isn't actually returning anything. The return value is trapped inside a promise. Luckily we can convert the function to use async/await
. So it should look something like this:
pickLocalKey = async (key, defaultValue) => {
try {
let value = await AsyncStorage.getItem(key);
const item = (value !== null && value !== undefined) ? value : defaultValue;
return item === 'true'; // this will make sure we get a Boolean back as long as you store strings 'true' and 'false' for your Booleans
} catch (error) {
return defaultValue;
}
}
Note that await
can throw so it is always best to wrap it in a try/catch
.
Then in your componentDidMount
you should make the calls to retrieve the values from AsyncStorage
async componentDidMount () {
// these won't throw as we are catching the error before it gets here
let anonymityChecked = await this.pickLocalKey('anonymity', true);
let locationChecked = await this.pickLocalKey('location', false);
let notificationChecked = await this.pickLocalKey('notification', false);
let statisticsChecked = await this.pickLocalKey('statistics', false);
// set all items in state in one go as multiple calls to setState are not good
this.setState({
anonymityChecked,
locationChecked,
notificationChecked,
statisticsChecked,
loaded: true . // <- we set loaded to true
}, () => console.log(this.state)); // this is how you check state after it has been set
}
Then in your render
you should now use the fact that loaded
is now true to render the correct view:
render () {
if (this.state.loaded) {
return (
this is the view that you actually want to show
)
} else {
return (
this is the view that you will show when everything is loading
)
}
}
Here are some articles that are worth reading: