React native - Data fetch from Async storage before rendering the app component

Viewed 1428

I am creating an iOS app on React Native and want to support multi languages for the app. The application works when set the target language, but when I open the app again it shows the default language. I am trying to get the defined language from async storage but it does not reflect the changes and render the app component. I ma using context api for localization.

I use the Launchscreen.xib for showing the splash screen and hide this splash screen using react-native-splash-screen method SplashScreen.hide() in app.js useEffect hook and I want to set the previously chosen language before loading the app component.

Localization.js file -

import React, {createContext, useState} from 'react';
import * as RNLocalize from 'react-native-localize';
import LocalizedStrings from 'react-native-localization';
import AsyncStorage from '@react-native-community/async-storage';
import en from './translations/en.json';
import de from './translations/de.json';
import bg from './translations/bg.json';
import it from './translations/it.json';
import nl from './translations/nl.json';
import pl from './translations/pl.json';
import pt from './translations/pt.json';
import ru from './translations/ru.json';
import fr from './translations/fr.json';
import es from './translations/es.json';



const APP_LANGUAGE = 'appLanguage';
const DEFAULT_LANGUAGE = 'en';



const languages = {en, de, bg, it, nl, pl, pt, ru, fr, es};

const translations = new LocalizedStrings(languages);

export const LocalizationContext = createContext({
    translations,
    setAppLanguage: () => {},
    appLanguage: DEFAULT_LANGUAGE,
    initializeAppLanguage: () => {},
  });



export const LocalizationProvider = ({children}) => {
    const [appLanguage, setAppLanguage] = useState(DEFAULT_LANGUAGE);
   //console.log(appLanguage);
    const setLanguage = language => {
      translations.setLanguage(language);
      setAppLanguage(language);
      AsyncStorage.setItem(APP_LANGUAGE, language);
    };

    const initializeAppLanguage = async () => {
      const currentLanguage = await AsyncStorage.getItem(APP_LANGUAGE);
      console.log('language' , currentLanguage);
      if (currentLanguage === null) {
        let localeCode = DEFAULT_LANGUAGE;
        const supportedLocaleCodes = translations.getAvailableLanguages();
        const phoneLocaleCodes = RNLocalize.getLocales().map(
          locale => locale.languageCode,
        );
        phoneLocaleCodes.some(code => {
          if (supportedLocaleCodes.includes(code)) {
            localeCode = code;
            return true;
          }
        });
        setLanguage(localeCode);
      } else {
        setLanguage(currentLanguage);
      }
      onSuccess();
    };

    return (
      <LocalizationContext.Provider
        value={{
          translations,
          setAppLanguage: setLanguage,
          appLanguage,
          initializeAppLanguage,
        }}>
        {children}
      </LocalizationContext.Provider>
    );
  };

App.js file

import React, {useEffect, useContext, useState} from 'react';
import {createStore, combineReducers, applyMiddleware} from 'redux';
import {composeWithDevTools} from 'redux-devtools-extension';
import {Provider} from 'react-redux';
import logger from 'redux-logger';
import ReduxThunk from 'redux-thunk';

import loginReducer from '_store/reducers/login.js';
import transportReducer from '_store/reducers/transport.js';
import gpsReducer from '_store/reducers/gpslogger.js';

import AppNavigator from '_navigations/AppNavigator';

import SplashScreen from 'react-native-splash-screen';
import FlashMessage from 'react-native-flash-message';
import {LocalizationContext} from './Localization';


const rootReducer = combineReducers({
  auth: loginReducer,
  transport: transportReducer,
  gps:  gpsReducer
});

const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(logger, ReduxThunk)));

const App = props => {
  const {initializeAppLanguage, appLanguage} = useContext(LocalizationContext);
  const [loaded, setLoaded] = useState(false);
  useEffect(() => {
    initializeAppLanguage();
    SplashScreen.hide();


  }, []);

  return (
   <Provider store={store}>

      <AppNavigator />

      <FlashMessage />
    </Provider>
  );
};

export default App;

How can I achieve the required behaviour?

2 Answers

I solved it by creating a separate splash screen and putting a setTimeout method there. it gives time to fetch the data from async storage and then navigate to main app.

We are doing sth that achieve the same wanted behavior in app.js

setI18nConfig = () => {
    const {dispatch} = this.store;
    const translationGetters = {
      ar: () => require("../src/assets/translations/ar.json"),
      en: () => require("../src/assets/translations/en.json"),
    };

    const translate = helpers.translate();

    const fallback = { languageTag: "en", isRTL: false };

    const { languageTag, isRTL } = this.appLanguage ?  this.appLanguage  : (RNLocalize.findBestAvailableLanguage(Object.keys(translationGetters)) || fallback)

    dispatch(setAppLanguage({
      languageTag,
      isRTL
    }));

    if(!this.appLanguage) {
      this.appLanguage = {languageTag, isRTL}
    }

    // clear translation cache
    translate.cache.clear();

    // set i18n-js config
    i18n.translations = { [languageTag]: translationGetters[languageTag]() };
    i18n.locale = languageTag;
  }
Related