4

My React Component stores user's settings in the AsyncStorage and get's them on load. The storing and getting data works well, but there is a problem that components renders before the values has been got from the async function. How this could be fixed? This is my code:

import React from 'react';
import { View } from 'react-native';
import AsyncStorage from '@react-native-community/async-storage';
import { Media } from '../constants';

class SettingsScreen extends React.Component {
  
  constructor(props) {
    super(props);
    const settings = ['appLanguage', 'colorTheme'];
    for(const i of settings) {
      this.getData(i).then(
        value => {
          if(value === null || value === undefined) {
            this.state[i] = Media.initalSettings[i];
          }
          else {
            this.state[i] = value;
          }
        }
      );
    }
  }

  async storeData(key, value) {
    try {
      if(key[0] != '@') {
        key = '@' + key;
      }
      await AsyncStorage.setItem(key, value);
    } 
    catch(e) {
      console.warn(e);
    }
  }

  async getData(key) {
    try {
      if(key[0] != '@') {
        key = '@' + key;
      }
      const value = await AsyncStorage.getItem(key);
      return value;
    } 
    catch(e) {
      console.warn(e);
    }
  }

  render() {

    const { appLanguage } = this.state;

    return (
      <>
        <View>
          {appLanguage}
        </View>
      </>
    )
  }
}

export default SettingsScreen;
5
  • I would have default values in the component, then pass them in once the async function is complete. This way it will re-render once they get updated. Commented Jul 29, 2020 at 12:54
  • In general, if you don't get the required params for a component before rendering it, you should probably use a conditional rendering in that component (i.e. check if the required params are present, if they are, render normally, if they're not, render a loading or null) Commented Jul 29, 2020 at 13:01
  • @rhigdon, I've tried to move this.state[i] = Media.initalSettings[i] before calling the async function, but nothing has changed Commented Jul 29, 2020 at 13:02
  • You should never set the state directly. You should use the setState helper function. Commented Jul 29, 2020 at 13:04
  • @rhigdon, thank for your advice, I though that calling setState is forbidden in the component's constructor, but I changed this.state[i] = value to this.setState({key: value}) after getting the data and it worked right. You can write an answer and I'll mark it as a solution Commented Jul 29, 2020 at 13:12

2 Answers 2

1

your constructor is way too cluttered.

initialize an empty state variable and populate it when page loads

constructor(props){
  super(props)
  
  this.state = {
    storedValue = ""
  }
}

async componentDidMount() {
  const storedValue = await AsyncStorage.getItem("settings");
  this.setState({storedValue})
}

render(){
  return (
    <View>
      <Text>{storedValue}</Text>
    </View>
  )
}
Sign up to request clarification or add additional context in comments.

Comments

0

It looks like you are trying to set the state directly in the constructor. Try this:

  constructor(props) {
    super(props);
    const settings = ['appLanguage', 'colorTheme'];
    for(const i of settings) {
      this.getData(i).then(
        value => {
          if(value === null || value === undefined) {
            this.state[i] = Media.initalSettings[i];
          }
          else {
            this.setState({key: value})
          }
        }
      );
    }
  }

Here is some documentation: https://reactjs.org/docs/state-and-lifecycle.html#do-not-modify-state-directly

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.