0

I have one functional component

import { DEFAULT_LANGUAGE} from './translations';
import React, { useState } from 'react';

export const Welcome= () => {
    const [appLanguage, setAppLanguage] = useState(DEFAULT_LANGUAGE);
    console.log(appLanguage);
};

translations.js

   import AsyncStorage from '@react-native-async-storage/async-storage';

    export const DEFAULT_LANGUAGE= async () => {
       try {
           const response = await AsyncStorage.getItem('appLanguage');
           if(response){
               return response;
           }
           else{
               return 'English';
           }
       }
       catch (e) {
           return 'English';
       }
    }

appLanguage returns promise object instead of value. How can I perform asynchronous operations inside as a constant variable?

Kingslayer
  • 36
  • 8
  • 2
    `DEFAULT_LANGUAGE` is a function. You need to call it. To run an async function when your component mounts, use `useEffect` with an empty dependency array. –  Aug 31 '21 at 10:48
  • Can you do just `useState(await DEFAULT LANGUAGE())`? (You'd probably need to make `async Welcome` as well) – Greg Aug 31 '21 at 10:52
  • 1
    @Grzegorz - No, component functions are rquired to be synchronous. You can't use `await` in a synchronous function. – T.J. Crowder Aug 31 '21 at 10:56
  • @T.J.Crowder what if you made `const getDefLang = async () => await DEFAULT_LANGUAGE()` and used that in `useState` ? – Greg Aug 31 '21 at 11:07
  • 1
    @Grzegorz - Same problem, `async` functions always return promises, so you'd get a promise, not the value. [You **can't** get an asynchronous result synchronously](http://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call). – T.J. Crowder Aug 31 '21 at 11:08

1 Answers1

1

You can't make DEFAULT_LANGUAGE contain the fulfillment value rather than a promise unless you make loading the module exporting it wait until that asynchronous operation is complete. So you either:

  1. Delay completion of that module's load until the value is available

  2. Make it a promise and handle that where you use it

1 - Delaying module load using top-level await

You can only do that if your environment supports top-level await, which many do now.

To use that to delay loading of that module until the asynchronous request is complete, you'd do this:

export const DEFAULT_LANGUAGE = await (async () => {
    try {
        const response = await AsyncStorage.getItem('appLanguage');
        if (response) {
            return response;
        } else {
            return 'English';
        }
    } catch (e) {
        return 'English';
    }
})();

Which can also be written as:

export const DEFAULT_LANGUAGE = await (async () => {
    let language;
    try {
        language = await AsyncStorage.getItem("appLanguage");
    } catch (e) {
    }
    return language || "English";
})();

or even:

export const DEFAULT_LANGUAGE = await AsyncStorage.getItem("appLanguage")
    .catch(() => null)
    .then(language => language || "English");

That does the asynchronous work, waits for it, and then allows the module load to occur.

Naturally, delaying module load like that may not be the best choice, it's very situation-dependent. But it's your only option if you need DEFAULT_LANGUAGE to be the fulfillment value, not a promise.

2 - Making it a promise and waiting for it

Right now, you've made DEFAULT_LANGUAGE a function. Nothing calls it. Instead, call it and store the returned promise:

export const DEFAULT_LANGUAGE_PROMISE = (async () => {
   try {
       const response = await AsyncStorage.getItem('appLanguage');
       if (response) {
           return response;
       } else {
           return 'English';
       }
   } catch (e) {
       return 'English';
   }
})();

Then, when using it, wait for promise fulfillment:

import { DEFAULT_LANGUAGE_PROMISE} from './translations';
import React, { useState } from 'react';

export const Welcome = () => {
    const [appLanguage, setAppLanguage] = useState(null);
    useEffect(() => {
        // We don't have to worry about rejection, because you've set it
        // up not to reject
        DEFAULT_LANGUAGE_PROMISE.then(setAppLanguage);
    }, []);
    // ...
};

Note that Welcome's rendering will have to handle the fact that appLanguage will be null at first in this scenario.

You might want to make your main entry-point component handle this, and then provide it to all components in the tree via context. There are all sorts of different ways to spin this.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875