Async Storage in PURE React Native
Tags: typescript, javascript, reactnative, silvenleaf
Published: 2022-05-06 01:41:43

Let's learn how to integrate Async Storage with PURE React Native.

### Step 1: Install async storage
First let's install async storage with the following command
```bash
npm i @react-native-async-storage/async-storage
```
Async storage is similar to Local storage  but it is meant for the apps whereas Local storage is for the web.

### Step 2: CRUD with async storage
##### ***Async Store (Helper file)***
```typescript
import AsyncStorage from "@react-native-async-storage/async-storage";

// Storing Data
const storeData = async (storageKey: string, value: any) => {
  try {
    const jsonValue = typeof value === 'string' ? value : JSON.stringify(value);
    await AsyncStorage.setItem(storageKey, jsonValue);

    return { msg: `Saving successful` };
  } catch (error) {
    // saving error
    console.log(error);
    return { error: true, msg: `Saving failed` };
  }
}

// Getting Data
const getStringValue = async (storageKey: string) => {
  try {
    const value = await AsyncStorage.getItem(storageKey)
    return value
  } catch (error) {
    // error reading value
    console.log(error);
  }
}
const getObjectValue = async (storageKey: string) => {
  try {
    const jsonValue = await AsyncStorage.getItem(storageKey)
    return jsonValue !== null ? JSON.parse(jsonValue) : null;
  } catch (error) {
    // error reading value
    console.log(error);
  }
}

// Updating Data
const updateObjectData = async (storageKey: string, value: any) => {
  try {
    const jsonValue = typeof value === 'string' ? value : JSON.stringify(value);
    await AsyncStorage.mergeItem!(storageKey, jsonValue);

    const newData = typeof value === 'string' ? await getStringValue(storageKey) : await getObjectValue(storageKey);

    return { msg: `Updating successful`, data: newData };
  } catch (error) {
    // updating error
    console.log(error);
    return { error: true, msg: `Updating failed` };
  }
}
const upsertObjectData = async (storageKey: string, value: any) => {
  try {
    const jsonValue = typeof value === 'string' ? value : JSON.stringify(value);
    const oldValue = await AsyncStorage.getItem(storageKey);

    if (oldValue === null) {
      await storeData(storageKey, value);
    } else {
      await AsyncStorage.mergeItem!(storageKey, jsonValue);
    }

    const newData = typeof value === 'string' ? await getStringValue(storageKey) : await getObjectValue(storageKey);

    return { msg: `Updating successful`, data: newData };
  } catch (error) {
    // upserting error
    console.log(error);
    return { error: true, msg: `Updating failed` };
  }
}

// Remove Data
const removeData = async (storageKey: string) => {
  try {

    await AsyncStorage.removeItem(storageKey);
    return { msg: `Removing successful` };

  } catch (error) {
    // removing error
    console.log(error);
    return { error: true, msg: `Removing failed` };
  }
}


// MUTLI FUNCS
const multiGetData = async (storageKeys: string[]) => {
  try {

    const valuesArray = await AsyncStorage.multiGet(storageKeys)
    return valuesArray;

  } catch (error) {
    // multi getting error
    console.log(error)
  }
}
const multiSetData = async (keyValueArray: [string,string][]) => {
  try {
    /*
      keyValueAray: [
       ["@MyApp_user", "value_1"],
       ["@MyApp_user", "value_1"]
     ]
    */
    const valuesArray = await AsyncStorage.multiSet(keyValueArray)
    return valuesArray;

  } catch (error) {
    console.log(error)
  }
}
const multiUpdateData = async (keyValueArray: [string,string][], value: any) => {
  try {
    /*
      keyValueAray: [
       ["@MyApp_user", "value_1"],
       ["@MyApp_user", "value_1"]
     ]
    */

    await AsyncStorage.multiMerge!(keyValueArray);
    const keys = keyValueArray.map(item => item[0]);
    const newMultiData = await multiGetData(keys);

    return { msg: `Updating successful`, data: newMultiData };
  } catch (error) {
    // multi updating error
    console.log(error);
    return { error: true, msg: `Updating failed` };
  }
}
const multiRemoveData = async (storageKeys: string[]) => {
  try {

    await AsyncStorage.multiRemove(storageKeys)
    return { msg: `Removing successful` };

  } catch (error) {
    // multi removing error
    console.log(error);
    return { error: true, msg: `Removing failed` };
  }
}

// SPECIALS
const getAllStorageKeys = async () => {
  try {

    const keys = await AsyncStorage.getAllKeys()
    return keys;

  } catch (error) {
    // read key error
    console.log(error)
  }
}
const clearStore = async () => {
  try {
    await AsyncStorage.clear();
    return { msg: `Store clearing successful` };
  } catch (error) {
    // clearing error
    console.log(error);
    return { error: true, msg: `Store clearing failed` };
  }
}

export const AsyncStoreKeyMap = {
  appSettings: 'appSettings',
  userProfile: 'userProfile',
  userProgress: 'userProgress',
}

const AsyncStore = {
  storeData,
  getStringValue, getObjectValue,
  updateObjectData, upsertObjectData,
  removeData,

  multiSetData,
  multiGetData,
  multiUpdateData,
  multiRemoveData,

  getAllStorageKeys,
  clearStore,
};

export default AsyncStore;
```

### Step 3: How to use?
```typescript
import AsyncStore, { AsyncStoreKeyMap } from '../../../utils/AsyncStore';

AsyncStore.upsertObjectData(AsyncStoreKeyMap.appSettings, { themeColor: color });
AsyncStore.upsertObjectData(AsyncStoreKeyMap.appSettings, { isDarkTheme: !props.isDarkTheme });
AsyncStore.upsertObjectData(AsyncStoreKeyMap.appSettings, { isSoundOn: !props.isSoundOn });
```
How to use with Redux in a React Native Component?
```typescript
// ___________________ root
import React from 'react';
import { connect } from 'react-redux';
import { NavigationProp } from '@react-navigation/native';

// ___________________ helpers
import Toast from 'react-native-toast-message';
import { rootVariables } from '../../rootStyles/variables';

// ___________________ async store
import AsyncStore, { AsyncStoreKeyMap } from '../../../utils/AsyncStore';

// ___________________ redux store
import { IRootState } from '../../store/store';
import { IAppSettingState } from '../../store/reducers/appSettingsReducer';

import appSettingsActions from '../../store/actions/appSettingsAction';
import userProgressActions from '../../store/actions/userProgressAction';



interface propsInterface extends IAppSettingState {
  navigation: NavigationProp<any>,
  app: {
    updateThemeColor: typeof appSettingsActions.updateThemeColor,
    updateIsDarkTheme: typeof appSettingsActions.updateIsDarkTheme,
    updateIsVibrationOn: typeof appSettingsActions.updateIsVibrationOn,
    updateIsSoundOn: typeof appSettingsActions.updateIsSoundOn,
    resetDefault: typeof appSettingsActions.resetDefault,
  },
  progress: {
    updateRank: typeof userProgressActions.updateRank,
    updateLevel: typeof userProgressActions.updateLevel,
    updateXP: typeof userProgressActions.updateXP,
    resetDefault: typeof userProgressActions.resetDefault,
  },
}



function SettingScreen(props: propsInterface) {
  const updateThemeColor = async (color: string) => {
    props.app.updateThemeColor(color);
    AsyncStore.upsertObjectData(AsyncStoreKeyMap.appSettings, { themeColor: color });
  }

  const updateIsDarkTheme = async () => {
    props.app.updateIsDarkTheme(!props.isDarkTheme);
    AsyncStore.upsertObjectData(AsyncStoreKeyMap.appSettings, { isDarkTheme: !props.isDarkTheme });
  }

  const updateIsSoundOn = async () => {
    props.app.updateIsSoundOn(!props.isSoundOn);
    AsyncStore.upsertObjectData(AsyncStoreKeyMap.appSettings, { isSoundOn: !props.isSoundOn });
  }


  const handleResetDefault = () => {
    props.app.resetDefault();
    AsyncStore.removeData(AsyncStoreKeyMap.appSettings);

    Toast.show({
      type: 'success',
      text1: 'Restore Default Successful',
      text2: 'Restore all default settings',
      topOffset: rootVariables.toastTopOffset,

    })
  }

  const handleResetProgress = () => {
    props.progress.resetDefault();
    AsyncStore.removeData(AsyncStoreKeyMap.userProgress);

    Toast.show({
      type: 'success',
      text1: 'Reset Progress Successful',
      text2: 'Reset all of your progress',
      topOffset: rootVariables.toastTopOffset,
    })
  }

  return (
    <SettingScreen/>
  );
}


const mapStateToProps = (state: IRootState) => {
  return {
    ...state.app,
  }
}

const mapDispatchToProps = (dispatch: any) => {
  return {
    app: {
      updateThemeColor: (color: string) => dispatch(appSettingsActions.updateThemeColor(color)),
      updateIsDarkTheme: (isDarkTheme: boolean) => dispatch(appSettingsActions.updateIsDarkTheme(isDarkTheme)),
      updateIsVibrationOn: (isVibrationOn: boolean) => dispatch(appSettingsActions.updateIsVibrationOn(isVibrationOn)),
      updateIsSoundOn: (isSoundOn: boolean) => dispatch(appSettingsActions.updateIsSoundOn(isSoundOn)),
      resetDefault: () => dispatch(appSettingsActions.resetDefault()),
    },

    progress: {
      updateRank: (rank: string) => dispatch(userProgressActions.updateRank(rank)),
      updateLevel: (level: number) => dispatch(userProgressActions.updateLevel(level)),
      updateXP: (xp: number) => dispatch(userProgressActions.updateXP(xp)),
      resetDefault: () => dispatch(userProgressActions.resetDefault()),
    },
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(SettingScreen)
```


**NEXT blog is coming by May 27th**

# What's NEXT?
#### 1. Scrolling with PURE React Native
#### 2. Project with Pure React Native
#### 3. More on App Development
#### 4. How to deploy to playstore
#### 5. Insane stuff with JavaScript/TypeScript
#### 6. Writing Automated Tests for any Server
#### 7. How to create an Android APP with NO XP with Expo
(including apk generating)


# Got any doubt? 
Drop a comment or Feel free to reach out to me @SilveLEAF on Twitter or [Linkedin](https://www.linkedin.com/in/silvenleaf/)

Wanna know more about me? Come here! 
[SilvenLEAF.github.io](https://SilvenLEAF.github.io)