43

I am using AsyncStorage in ComponentWillMount to get locally stored accessToken, but it is returning the promise after render() function has run. How can I make render() wait until promise is completed? Thank you.

4
  • 1
    What exactly are you trying to do with the data? One way to work around this is to store an empty variable in the getInitalState function, then when AsyncStorage returns the value, set the state with the new value. Commented Nov 5, 2015 at 20:25
  • Thanks for the question, I'm getting locally stored accessToken from AsyncStorage to set the state. But since it's returning a promise, render() function gets ran first then state is set immediately after. So I see log-in page briefly (~0.1s) every time I visit the app since render() checks for state and returns log-in page or main app. Commented Nov 5, 2015 at 22:03
  • 1
    Oh, ok. Not sure how to fix that, other than to maybe show a loader / loading message then replace it with either the login view or the logged in view. Commented Nov 5, 2015 at 22:25
  • Thanks for the suggestion--I initiated a new state of loginLoaded as false and when data was loaded, set it to true. Render() shows an empty view if loginLoaded is false, so now that brief login button is not seen! Commented Nov 6, 2015 at 22:37

4 Answers 4

72

You can't make a component wait to render, as far as I know. What I've done in the app I'm working on is to add a loading screen until that promise from AsyncStorage resolves. See the examples below:

//
// With class component syntax
//

import React from 'react';
import {
  AsyncStorage,
  View,
  Text
} from 'react-native';

class Screen extends React.Component {

  state = {
    isLoading: true
  };

  componentDidMount() {
    AsyncStorage.getItem('accessToken').then((token) => {
      this.setState({
        isLoading: false
      });
    });
  },

  render() {
    if (this.state.isLoading) {
      return <View><Text>Loading...</Text></View>;
    }
    // this is the content you want to show after the promise has resolved
    return <View/>;
  }

}
//
// With function component syntax and hooks (preferred)
//

import React, { useEffect } from 'react';
import {
  AsyncStorage,
  View,
  Text
} from 'react-native';

const Screen () => {

  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    AsyncStorage.getItem('accessToken').then((token) => {
      setIsLoading(false);
    });
  }, [])

  if (isLoading) {
    return <View><Text>Loading...</Text></View>;
  }
  // this is the content you want to show after the promise has resolved
  return <View/>;

}

Setting the isLoading property in state will cause a re-render and then you can show the content that relies on the accessToken.

On a side note, I've written a little library called react-native-simple-store that simplifies managing data in AsyncStorage. Hope you find it useful.

Sign up to request clarification or add additional context in comments.

10 Comments

Does this still work in the latest version of React Native? I've implemented this code exactly but it just gets stuck on 'Loading...' forever. If I run a console log on render to show isLoading (without the if method) it returns, false and then true so theoretically it should be working. But with the if method enabled its stuck on 'Loading' forever and the log only returns false.
I'm having the same issue as Hasen above. My render() function is never called a second time, after the state is updated. So my loading screen just shows indefinitely....
I had the same trouble, i keep stuck on loading screen. Did anyone get a way to solve this?
I think someone forgot to point out that setting state in componentWillMount method is a bad idea. See here
For me it works in the latest React Native version. Thank you so much.
|
10

Based on react-native doc, you can do something like this:

import React, { Component } from 'react';
import {
  View,
} from 'react-native';

let STORAGE_KEY = '@AsyncStorageExample:key';

export default class MyApp extends Component {

  constructor(props) {
    super(props);
    this.state = {
      loaded: 'false',
    };
  }

  _setValue = async () => {
    try {
      await AsyncStorage.setItem(STORAGE_KEY, 'true');
    } catch (error) { // log the error
    }
  };

  _loadInitialState = async () => {
    try {
      let value = await AsyncStorage.getItem(STORAGE_KEY);
      if (value === 'true'){
        this.setState({loaded: 'true'});
      } else {
        this.setState({loaded: 'false'});
        this._setValue();
      }
    } catch (error) {
      this.setState({loaded: 'false'});
      this._setValue();
    }
  };

  componentWillMount() {
    this._loadInitialState().done();
  }

  render() {
    if (this.state.loaded === 'false') {
      return (
        <View><Text>Loading...</Text></View>
      );
    }
    return (
      <View><Text>Main Page</Text></View>
    );
  }
}

1 Comment

[ts] Property 'done' does not exist on type 'Promise<void>'.
0

you can use react-native-easy-app that is easier to use than async storage. this library is great that uses async storage to save data asynchronously and uses memory to load and save data instantly synchronously, so we save data async to memory and use in app sync, so this is great.

import { XStorage } from 'react-native-easy-app';
import { AsyncStorage } from 'react-native';

const initCallback = () => {

     // From now on, you can write or read the variables in RNStorage synchronously

     // equal to [console.log(await AsyncStorage.getItem('isShow'))]
     console.log(RNStorage.isShow); 

     // equal to [ await AsyncStorage.setItem('token',TOKEN1343DN23IDD3PJ2DBF3==') ]
     RNStorage.token = 'TOKEN1343DN23IDD3PJ2DBF3=='; 

     // equal to [ await AsyncStorage.setItem('userInfo',JSON.stringify({ name:'rufeng', age:30})) ]
     RNStorage.userInfo = {name: 'rufeng', age: 30}; 
};

XStorage.initStorage(RNStorage, AsyncStorage, initCallback); 

Comments

-5

React-native is based on Javascript which does not support blocking functions.Also this makes sense as we don't want the UI to get stuck or seem unresponsive. What you can do is handles this in the render function. i.e Have a loading screen re-render it as you as you get the info from the AsyncStorage

1 Comment

Updating a state variable will "re-render", meaning it will call the render function again

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.