简体   繁体   中英

React Native Issue - Initialize state by calling another method from constructor

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:

Replace

pickLocalKey(key, defaultValue) {
  AsyncStorage.getItem(key).then((value) => {
    const item = (value !== null && value !== undefined) ? value : defaultValue;
    console.log(item);
    return item;
  });
}

with

constructor(props) {
    super(props);
    this.pickLocalKey('anonymity', true, 'anonymityChecked');
    this.pickLocalKey('location', false, 'locationChecked');
    this.pickLocalKey('notification', false, 'notificationChecked');
    this.pickLocalKey('statistics', false, 'statisticsChecked')
    this.state = {
    };
}

pickLocalKey = (key, defaultValue, stateKey) => {
  AsyncStorage.getItem(key).then((value) => {
    const item = (value !== null && value !== undefined) ? value : defaultValue;
    console.log(item);
    this.setState({
     [stateKey]: item
    });

  });
}

render() {
 console.log(this.state);
}

EDIT: So the issue is this a promise, now constructor run for once, not gonna wait for your promise to resolve. Also you cant or dont wanna make your constructor a promise. Once you get the data back, set the data not possible. But you call the function then do setState. Then see the state in render, I hope you will see the right state.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM