0

I want to export an objects based on conditions for example first I check some which language stored in 'AsyncStorage' then based on languages I return a particular object.

file: lang.js

import AsyncStorage from '@react-native-community/async-storage';
let exportingLang = {};

AsyncStorage.getItem('@lang').then(language=> {
    if(language){
        if(language === "hindi") exportingLang = {
           name: 'a hindi name'
        };
        else if(language === "panjabi") exportingLang = {
           name: 'a panjabi name'
        };
        else exportingLang = {
           name: 'a english name'
        };;
    }
});

export const Lang = exportingLang;

file: post.js

import {Lang} from 'lang';

export default Post = props => {

    return (
        <Text>{Lang.name}</Text>
    );
}

but the above code does not work.

Afraz Hussain
  • 2,183
  • 1
  • 11
  • 17
Muhammad
  • 2,572
  • 4
  • 24
  • 46

2 Answers2

1

Why not store the value of exportingLang in state, and then use that in your component?

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

export default Post = props => {

    const [name, setName] = useState()

    AsyncStorage.getItem('@lang').then(language=> {
        if(language){
            if(language === "hindi") {
               setName('a hindi name')
            } else if(language === "panjabi") {
               setName('a punjabi name')
            } else {
               setName('an english name')
            };
        }
    });


    return (
        <Text>{name}</Text>
    );
}
Seth Lutske
  • 9,154
  • 5
  • 29
  • 78
1

The first thing to note here is that getItem returns a JavaScript Promise according to the documentation. With the way JavaScript works, we cannot get the values out of Promises and export them, without resolving or rejecting the Promise, more on that here. To explain it in terms of your example, you could assign a value to the exportingLang, but it will almost certainly return the initial empty object {} that you initialized it to because the Promise assignment happens asynchronously, that is, it needs to wait for the storage system to find the @lang key, then return its value which can take a while (hence the need for a Promise).

If the point of having the lang.js file is to avoid typing the if statements over and over again, then that's all fine (otherwise I would just use the getItem Promise once in the Component I need the value in). I would first make sure that we export the results returned from the Promise as follows:

lang.js

export default AsyncStorage.getItem('@lang').then(language => {
  let exportingLang = {name: 'default'};
  if (language) {
    if (language === 'hindi')
      exportingLang = {
        name: 'a hindi name',
      };
    else if (language === 'panjabi')
      exportingLang = {
        name: 'a panjabi name',
      };
    else
      exportingLang = {
        name: 'a english name',
      };
  }
  return exportingLang;
});

This would in-turn give me another Promise as an export.

Secondly, you cannot use values directly from a Promise into a React render method. You need to assign it to a state using useState or useEffect. You can read more on this here.

For simplicity's sake, I have used it in a Component as follows:

import langPromise from './lang';

export default class App extends Component {
  state = {
    lang: { name: '' },
  };

  async componentDidMount() {
    const lang = await langPromise.then(lang => this.setState({ lang }));
  }

  render() {
    return (
      <View>
        <Text>{this.state.lang.name}</Text>
      </View>
    );
  }
}

Please let me know if you have any further questions.

Afraz Hussain
  • 2,183
  • 1
  • 11
  • 17
  • I've also created a working snack for you at: https://snack.expo.io/rySQiEgIU Please note that this might have the older version of the `AsyncStorage` but the idea is still the same. – Afraz Hussain Mar 19 '20 at 00:24
  • Thank you very much, but I actually need something before render instead of after render, here as you worked I can access the object after render that I dont want. – Muhammad Mar 19 '20 at 05:14
  • 2
    Afraz's answer is quite elegant - I like it. My answer below accomplishes the same thing. What do you mean you want to access the object "before render" instead of after render? In both of these answer, the component renders, and once your AsyncStorage function returns a value, it causes a state change which causes a rerender. Where in the component lifecycle do you envision needing the value that AsyncStorage returns? – Seth Lutske Mar 19 '20 at 15:13
  • 1
    @Muhammad Unfortunately, you cannot stop a component from rendering and make it wait for the Promise to return a value, exactly as Seth has explained in his comment above. I had also linked a perfect SO question in my answer regarding the same issue. *IF you're still not convinced and you really have to get your value before render then you can use `UNSAFE_componentWillMount()` to have your async method in.* I absolutely wouldn't recommend it, but you can read more about that in this perfect blog: https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#fetching-external-data – Afraz Hussain Mar 20 '20 at 13:31