Newby Coder header banner

React Native Local Storage

Using Local Storage in React Native

Local storage can be implemented in a React Native application so that application data can be persisted when it is closed and reopened

React Native provides AsyncStorage to implement data storage

Example provided uses Storage component of react-native-storage package, which is awrapper over AsyncStorage, to store the index of a selected tab

Usage

Import
// imports for implementing local storage
import Storage from 'react-native-storage';
import AsyncStorage from '@react-native-community/async-storage';
// tabview is specific to provided example
import ScrollableTabView, { ScrollableTabBar } from 'react-native-scrollable-tab-view';

Declare Storage

Declare storage, preferably as a global variable

storageBackend is set to AsyncStorage enabling the data to persist

Function declared in sync is to be called when data for corresponding key (identifiers) is not found in storage

const storage = new Storage({
  // maximum capacity of key-value pairs
  // size: 1000,

  // Use AsyncStorage for React Native apps
  // If storageBackend is not set, data is lost after reload
  storageBackend: AsyncStorage,

  // default expire time in milliseconds, null to not expire
  // defaultExpires: 1000 * 3600 * 24,

  // cache data in the memory
  enableCache: true,

  // if data is not found in storage or expired data is found,
  // the corresponding sync method is invoked to get data
  sync: {
    // name of sync method should be same as 'key' of data stored
    // this method can include network call
    tabIndex() {
      console.log('sync call');
      return 0;
    }
  }
});

Store data in storage

Use storage.save() method to store data for a key, where data can be an object

storage.save({
    key: 'tabIndex', // Do not use underscore("_") in key
    data: index.i,
    // expire time in milliseconds, null to not expire
    // expires: 1000 * 3600
});

Retrieve data from storage

Use storage.load() method to get data for a key

storage
    .load({
      key: 'tabIndex',
      // autoSync (default: true) - if data is not found or has expired,
      // then corresponding sync method is invoked
      // autoSync: true,

      // if syncInBackground is false then expired data is not returned while syncing
      // syncInBackground: false,
    })
    .then(index => { this.setState({tabIndex:index})})
    .catch(err => {
      console.error(err);
      if(!this.state.tabIndex)
        this.setState({tabIndex:0}); //precautionary
      }
)

Creating new React Native App

Check React Native Installation for installation related info

Use react-native init command to create a new React Native project (here named LocalStorageApp)

react-native init LocalStorageApp

This creates a directory named LocalStorageApp and initializes it as a react native application directory

This can be skipped in case an existing react native project is being integrated into


Dependency

Since this example uses ScrollableTabView component, react-native-scrollable-tab-view package has to be added

Go to project directory (here LocalStorageApp)

cd LocalStorageApp

Add react-native-storage, @react-native-community/async-storage and react-native-scrollable-tab-view to project using yarn

yarn add react-native-storage @react-native-community/async-storage react-native-scrollable-tab-view

or using npm

npm install react-native-storage @react-native-community/async-storage react-native-scrollable-tab-view --save

iOS

After yarn add command, run pod install command in ios folder inside project directory to install any required CocoaPods dependencies

cd ios && pod install && cd ..

App Code

In App.js a ScrollableTabView is declared to show different tabs

Since storage methods are async, a progress indicator is shown till an initial value for tab index can be set

When app is opened first time, tabIndex() method of sync is called since storage doesn't contain value for key tabIndex

For subsequent restarts of the application, index is retrieved from storage

App.js
import React from 'react';
import { ActivityIndicator, StyleSheet, Text, View, Image } from 'react-native';
import ScrollableTabView, { ScrollableTabBar } from 'react-native-scrollable-tab-view';
import Storage from 'react-native-storage';
import AsyncStorage from '@react-native-community/async-storage';


const storage = new Storage({
  storageBackend: AsyncStorage,
  enableCache: true,
  sync: {
    tabIndex() {
      return 0;
    }
  }
});


export default class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      tabIndex: null,
    };
  }

  componentDidMount() {
    this.getIndexFromStorage();
  }

  async getIndexFromStorage() {
    storage.load({key: 'tabIndex'})
      .then(index => { this.setState({tabIndex:index})})
      .catch(err => {
        console.error(err);
        if(!this.state.tabIndex)
          this.setState({tabIndex:0}); //precautionary
        }
      )
  };

  tabChanged = (index) => {
    // store tab index in storage
    storage.save({
      key: 'tabIndex',
      data: index.i,
    });
    // setState is not called
    this.state.tabIndex = index.i;
  };

  renderTabView(index) {
    return (
      <ScrollableTabView
        style={{ marginTop: 20 }}
        initialPage={index}
        onChangeTab={this.tabChanged}
        renderTabBar={() => <ScrollableTabBar />}
      >
        <View style={[styles.tab, {backgroundColor: 'orange'}]} tabLabel='Tab #1'><Text>Eat</Text></View>
        <View style={[styles.tab, {backgroundColor: 'purple'}]} tabLabel='Tab #2'><Text>Sleep</Text></View>
        <View style={[styles.tab, {backgroundColor: 'dodgerblue'}]} tabLabel='Tab #3'><Text>Change tab</Text></View>
        <Image tabLabel='Tab #4' style={styles.image}  source={{uri:"https://resourceplus.files.wordpress.com/2015/01/image.jpg"}} />
      </ScrollableTabView>
    )
  }

  render() {
    const index = this.state.tabIndex;
    // show progress indicator till an initial value is set for tabIndex
    view = index !=null ? this.renderTabView(index) :
        <View style={styles.container}>
          <ActivityIndicator size="small" color="#00ff00" />
        </View>
    return (view)
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  tab: {
    alignItems: 'center',
    justifyContent: 'center',
    height: '100%',
  },
  image: {
    resizeMode: 'stretch',
    width: '100%',
    height: '100%'
  }
});

Run instructions

Running in Android

cd into project directory (here LocalStorageApp)

cd LocalStorageApp

Run metro server to serve js

react-native start

Go to project directory in another terminal tab

Enter following run command for Android:

react-native run-android
cl-react-native-local-storage

Running in ios

cd into project directory (here LocalStorageApp)

cd LocalStorageApp

Enter following command :

react-native run-ios

This might take some time


If the app shows error about metro server not configured (check Running an App in iOS), then run metro server in another tab:

react-native start

Reload the app

Re-run react-native run-ios command if reloading doesn't work

cm-react-native-local-storage-as1cm-react-native-local-storage-as2cm-react-native-local-storage-as3
cm-react-native-local-storage-as4cm-react-native-local-storage-as5
cm-react-native-local-storage-as6cm-react-native-local-storage-as7