Easy OS Background Tasks in React Native

Thanks to a couple of relatively new libraries, running tasks in a background thread, also known as a service, when your react native app is closed has never been easier.

Today I’ll walk you through setting up tasks that will run periodically even when your app is closed. If you already have React Native setup, and you’re staring at your IDE right now, it will only take you ~15 minutes to be fully up and running with this complete example.

We will use two libraries to accomplish this:

In our example, we will do basic image pre-fetching (yeah yeah it’s a bit pointless but it’s easy to understand for illustrative purposes).

Examples of more realistic use cases for this functionality:

  • Downloading content for offline access.
  • Media processing.
  • Cache Warming.
  • Durable API calls to external services, such as publishing content to a variety of 3rd party distribution channel APIs.
  • Complex and time-consuming jobs that you want consistently processed regardless if app is open, closed, or repeatedly opened and closed.

Installation

First create the skeleton React Native app in your working directory

$ react-native init backgroundexample

Quickly install react-native-queue and react-native-background-task packages and link them (note react-native-background-fetch is an optional dependency of react-native-background-task required for iOS support).

$ yarn add react-native-queue
$ react-native link realm
$ yarn add react-native-background-task
$ react-native link react-native-background-task
$ yarn add [email protected]
$ react-native link react-native-background-fetch

Manually update the onCreate() method in MainApplication.java like so

// Update the following file as seen below
// android/app/src/main/java/com/backgroundexample/MainApplication.java@Override
public void onCreate() {
  super.onCreate();
  SoLoader.init(this, /* native exopackage */ false);
  BackgroundTaskPackage.useContext(this); // ADD ME HERE!
}

Building The Feature

First lets update the react native skeleton app to include a screen of images that is toggled with a button press. Nothing fancy.

Toggle Screen App.js Changes

Nothing fancy here. Just add ScrollView, Button, Image imports, modify the container style, add the image style, and make some small updates to the skeleton App class.

import React, { Component } from 'react';
import {
  Platform,
  StyleSheet,
  Text,
  ScrollView,
  Button,
  Image
} from 'react-native';

export default class App extends Component<{}> {

  constructor(props) {
    super(props);

    this.state = {
      showImages: false
    };

  }

  render() {
    return (
      <ScrollView style={styles.container}>
        <Button title={"Toggle Screen"} onPress={ () => { this.setState({ showImages: !this.state.showImages}) } } />
        {! this.state.showImages && <Text style={styles.welcome}>Home Screen</Text> }
        {this.state.showImages && <Text style={styles.welcome}>Image Screen</Text> }
        {this.state.showImages && <Image style={styles.image} source={{uri: 'https://i.imgur.com/kPkQTic.jpg'}} permanent={false} /> }
        {this.state.showImages && <Image style={styles.image} source={{uri: 'https://i.redd.it/uwvjph19mltz.jpg'}} permanent={false} /> }
        {this.state.showImages && <Image style={styles.image} source={{uri: 'https://i.redd.it/39w0xd9ersxz.jpg'}} permanent={false} /> }
      </ScrollView>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    padding: 10
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  image: {
    width:150,
    height: 204
  }
});

Integrating the background task

At the top of App.js we will define the js function we want the OS to call periodically in the background when the app is closed (the “background task”).

What we want to happen in this background task function, is to initialize the queue, and immediately start pulling jobs off of the queue and processing as many as we can before hitting the 30 second timeout imposed by iOS (Android does not have this timeout limitation but we need to adhere to the strictest constraints for cross-platform support) for background service functions. Because of this hard timeout limit, we will call queue.start(lifespan) with a lifespan of 25 seconds. This way, the queue will start processing jobs for at most 25 seconds (or until queue is cleared), then stop processing guaranteeing us time to call the required Background.finish() before the OS times out the function.

In our example, 25 seconds will be more than enough to churn through the entire queue, seeing as we’re only gonna pre-fetch 3 images. However, imagine if we were pre-fetching 10,000 images. The queue keeps the jobs durable (they won’t be deleted until completed, and can auto-retry on failure), so every ~15 min when the OS fires this function in the background again, another batch of images would be pre-fetched, and sooner or later all of the images would be pre-fetched all behind the scenes.

import BackgroundTask from 'react-native-background-task'
import queueFactory from 'react-native-queue';

BackgroundTask.define(async () => {

  // Init queue
  queue = await queueFactory();

  // Register job worker
  queue.addWorker('pre-fetch-image', async (id, payload) => {

    Image.prefetch(payload.imageUrl);

  });

  // Start the queue with a lifespan
  // IMPORTANT: OS background tasks are limited to 30 seconds or less.
  // NOTE: Queue lifespan logic will attempt to stop queue processing 500ms less than passed lifespan for a healthy shutdown buffer.
  // IMPORTANT: Queue processing started with a lifespan will ONLY process jobs that have a defined timeout set.
  // Additionally, lifespan processing will only process next job if job.timeout < (remainingLifespan - 500).
  await queue.start(25000); // Run queue for at most 25 seconds.

  // finish() must be called before OS hits timeout.
  BackgroundTask.finish();

});

Then add a componentDidMount() lifecycle method to the App component to schedule the background task when the app mounts.

componentDidMount() {
  BackgroundTask.schedule(); // Schedule the task to run every ~15 min if app is closed.
}

Your App.js file should now look something like this:

import React, { Component } from 'react';
import {
  Platform,
  StyleSheet,
  Text,
  ScrollView,
  Button,
  Image
} from 'react-native';

import BackgroundTask from 'react-native-background-task'
import queueFactory from 'react-native-queue';

BackgroundTask.define(async () => {

  // Init queue
  queue = await queueFactory();

  // Register job worker
  queue.addWorker('pre-fetch-image', async (id, payload) => {

    Image.prefetch(payload.imageUrl);

  });

  // Start the queue with a lifespan
  // IMPORTANT: OS background tasks are limited to 30 seconds or less.
  // NOTE: Queue lifespan logic will attempt to stop queue processing 500ms less than passed lifespan for a healthy shutdown buffer.
  // IMPORTANT: Queue processing started with a lifespan will ONLY process jobs that have a defined timeout set.
  // Additionally, lifespan processing will only process next job if job.timeout < (remainingLifespan - 500).
  await queue.start(25000); // Run queue for at most 25 seconds.

  // finish() must be called before OS hits timeout.
  BackgroundTask.finish();

});

export default class App extends Component<{}> {

  constructor(props) {
    super(props);

    this.state = {
      showImages: false
    };

  }

  componentDidMount() {
    BackgroundTask.schedule(); // Schedule the task to run every ~15 min if app is closed.
  }

  render() {
    return (
      <ScrollView style={styles.container}>
        <Button title={"Toggle Screen"} onPress={ () => { this.setState({ showImages: !this.state.showImages}) } } />
        {! this.state.showImages && <Text style={styles.welcome}>Home Screen</Text> }
        {this.state.showImages && <Text style={styles.welcome}>Image Screen</Text> }
        {this.state.showImages && <Image style={styles.image} source={{uri: 'https://i.imgur.com/kPkQTic.jpg'}} permanent={false} /> }
        {this.state.showImages && <Image style={styles.image} source={{uri: 'https://i.redd.it/uwvjph19mltz.jpg'}} permanent={false} /> }
        {this.state.showImages && <Image style={styles.image} source={{uri: 'https://i.redd.it/39w0xd9ersxz.jpg'}} permanent={false} /> }
      </ScrollView>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    padding: 10
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  image: {
    width:150,
    height: 204
  }
});

Adding Queue Jobs

Now that we’ve got our background task setup to initialize the queue and process jobs when our app is closed, we need a way to actually add jobs to the queue!

First we’ll initialize the queue in the app so we can use it to create jobs.

Reference the final App.js file below in lines 41–54 to make the necessary updates to the constructor() in order to initialize the queue inside the app.

After the queue is initialized, create the createPrefetchJobs() class method seen below in lines 60–86. Inside this method we will reference the queue instance stored in the app component state to create the 3 jobs that prefetch images to throw on the queue. Notice that we pass false as the last parameter to createJob(), this stops the queue from starting up processing immediately (which is the default behavior). In this example we don’t want the queue to process in the main app thread, so we’ll only call queue.start() in the background task.

Last but not least, update render() in line 92 to add the “Pre-fetch Images” button and wire it to the createPrefetchJobs() method we created earlier.

import React, { Component } from 'react';
import {
  Platform,
  StyleSheet,
  Text,
  ScrollView,
  Button,
  Image
} from 'react-native';

import BackgroundTask from 'react-native-background-task'
import queueFactory from 'react-native-queue';

BackgroundTask.define(async () => {

  // Init queue
  queue = await queueFactory();

  // Register job worker
  queue.addWorker('pre-fetch-image', async (id, payload) => {

    Image.prefetch(payload.imageUrl);

  });

  // Start the queue with a lifespan
  // IMPORTANT: OS background tasks are limited to 30 seconds or less.
  // NOTE: Queue lifespan logic will attempt to stop queue processing 500ms less than passed lifespan for a healthy shutdown buffer.
  // IMPORTANT: Queue processing started with a lifespan will ONLY process jobs that have a defined timeout set.
  // Additionally, lifespan processing will only process next job if job.timeout < (remainingLifespan - 500).
  await queue.start(25000); // Run queue for at most 25 seconds.

  // finish() must be called before OS hits timeout.
  BackgroundTask.finish();

});

export default class App extends Component<{}> {

  constructor(props) {
    super(props);

    this.state = {
      queue: null,
      showImages: false
    };

    queueFactory()
      .then(queue => {
        this.setState({queue});
      });

  }

  componentDidMount() {
    BackgroundTask.schedule(); // Schedule the task to run every ~15 min if app is closed.
  }

  createPrefetchJobs() {

    // Create the prefetch job for the first <Image> component.
    this.state.queue.createJob(
      'pre-fetch-image',
      { imageUrl: 'https://i.imgur.com/kPkQTic.jpg' }, // Supply the image url we want prefetched in this job to the payload.
      { attempts: 5, timeout: 15000 }, // Retry job on failure up to 5 times. Timeout job in 15 sec (prefetch is probably hanging if it takes that long).
      false // Must pass false as the last param so the queue starts up in the background task instead of immediately.
    );

    // Create the prefetch job for the second <Image> component.
    this.state.queue.createJob(
      'pre-fetch-image',
      { imageUrl: 'https://i.redd.it/uwvjph19mltz.jpg' }, // Supply the image url we want prefetched in this job to the payload.
      { attempts: 5, timeout: 15000 }, // Retry job on failure up to 5 times. Timeout job in 15 sec (prefetch is probably hanging if it takes that long).
      false // Must pass false as the last param so the queue starts up in the background task instead of immediately.
    );

    // Create the prefetch job for the third <Image> component.
    this.state.queue.createJob(
      'pre-fetch-image',
      { imageUrl: 'https://i.redd.it/39w0xd9ersxz.jpg' }, // Supply the image url we want prefetched in this job to the payload.
      { attempts: 5, timeout: 15000 }, // Retry job on failure up to 5 times. Timeout job in 15 sec (prefetch is probably hanging if it takes that long).
      false // Must pass false as the last param so the queue starts up in the background task instead of immediately.
    );

  }

  render() {
    return (
      <ScrollView style={styles.container}>
        <Button title={"Toggle Screen"} onPress={ () => { this.setState({ showImages: !this.state.showImages}) } } />
        {this.state.queue && <Button title={"Pre-fetch Images"} onPress={ this.createPrefetchJobs.bind(this) } />}
        {! this.state.showImages && <Text style={styles.welcome}>Home Screen</Text> }
        {this.state.showImages && <Text style={styles.welcome}>Image Screen</Text> }
        {this.state.showImages && <Image style={styles.image} source={{uri: 'https://i.imgur.com/kPkQTic.jpg'}} permanent={false} /> }
        {this.state.showImages && <Image style={styles.image} source={{uri: 'https://i.redd.it/uwvjph19mltz.jpg'}} permanent={false} /> }
        {this.state.showImages && <Image style={styles.image} source={{uri: 'https://i.redd.it/39w0xd9ersxz.jpg'}} permanent={false} /> }
      </ScrollView>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    padding: 10
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  image: {
    width:150,
    height: 204
  }
});

You’re Done!

Now boot up your react native app on an actual device (background tasks WILL NOT FIRE in simulators). Once the app is booted, click the prefetch button to queue the jobs.

Now all that’s left is to unfocus the app, and wait. OS Background tasks WILL NOT fire if the app is in focus (that would sort of be against the entire point). After ~15 minutes, the OS will fire up the background task, initialize the queue, and start the queue up churning through your 3 prefetch jobs.

At this point your remote images have been prefetched to the phone’s local disk, and when you click “toggle screen” to view the images, they will be pulled from your local disk instead of the network.

Responsive React Components

React is awesome and the whole world agrees to it unanimously. Development is a whole lot faster and easier when we look at everything as components. Since everything is JavaScript driven, React renders only the code that is necessary based on application’s state. It doesn’t matter if you have over a thousand components and tens of thousands of lines of code. If you lazy load your components, you load only what’s necessary for the user and I think that is the biggest win with using React.

That being said, have you ever wondered what happens when you write media queries in your React codebase?

You render elements that’s not supposed to be in a particular viewport and hide it using css.

In this example

import React from 'react';
import './Example.style.scss';

const Example = () => {
  return (
    <div className="example">
      <div className="mobile-only">I'm a fancy mobile div</div>
      <div className="desktop-only">I'm a heavy desktop div</div>
    </div>
  );
};

Example.style.scss file

.example {
  .mobile-only {
    @media (min-width: 768px){
      display: none;
    }
  }
  .desktop-only {
    @media (max-width: 767px){
      display: none;
    }
  }
}

When Example component renders, both .mobile-only and .desktop-onlyelements will be rendered in the DOM but .mobile-only div will be hidden in bigger viewports and .desktop-only div will be hidden in smaller viewports with css display: none.

This is okay if this is small. But in one of the projects I worked, we had a heavy desktop menu and a heavy mobile menu both rendered in all the viewports. Just the Menu alone should be around 20Kb in size each and we easily had an unwanted 20Kb being loaded into the browser for each user. If you have more viewport specific elements, this size is going to keep increasing.

React Socks is a minimal React library to render components based on viewport.

Say goodbye to media-queries. Here’s how you can render viewport specific components with an uber-cool syntax.

const Example = () => {
  return(
    <Breakpoint small down>
      <MyAwesomeMobileMenu>
        This component will render only in mobile devices
      </MyAwesomeMobileMenu>
    </Breakpoint>
  );
};
const Example = () => {
  return(
    <div>
      <Breakpoint small down>
        <div>I will render only in mobile devices</div>
      </Breakpoint>

      <Breakpoint medium only>
        <div>I will render only in tablets (iPad, etc...)</div>
      </Breakpoint>

      <Breakpoint large up>
        <div>I will render in laptops, desktops and everything bigger</div>
      </Breakpoint>
    </div>
  );
};

And that’s not just it. You can specify your own breakpoints (as many as you want wow!) and use them according to your project needs. Also, you will have to setDefaultBreakpoints only once in your project 😎

import { setDefaultBreakpoints } from 'react-socks';

setDefaultBreakpoints([
  { xs: 0 },
  { s: 376 },
  { m: 426 },
  { l: 769 },
  { xl: 1025 }
]);

These are my favourite breakpoints

setDefaultBreakpoints([
  { cats: 0 },
  { dinosaurs: 900 }
]);

<Breakpoint cats only>
    Only cats can render me 🐈
</Breakpoint>

Reasons why you should use React Socks

  • Render viewport specific components without hassle
  • You can define your own breakpoints (Eg. xs, ipad, bigmonitors) and use them
  • You can improve your app performance if you lazy load your viewport specific components
  • Simpler and sweeter syntax for ease of use

The library has been published to npm and is in alpha version. I’d love to get your feedback and improve it before releasing the first stable version.

Edit: The first stable version has been released on Dec 9, 2018 with performance improvements and SSR support. 🎉

If you’re wondering why the name React Socks 🤷‍♂️

React Socks wraps your components comfortably to prevent unnecessary render in various viewports, just like how you wrap your feet with socks to prevent cold feet

Let’s put some fancy React Socks on and wrap all the components

ACF: Add file preview to admin panel

By adding this to your functions.php file you will be able to make a document preview of your uploaded file to the ACF File Upload field:

function acf_change_icon_on_files ( $icon, $mime, $attachment_id ){ // Display thumbnail instead of document.png
		
		if ( strpos( $_SERVER[ 'REQUEST_URI' ], '/wp-admin/upload.php' ) === false && $mime === 'application/pdf' ){
			$get_image = wp_get_attachment_image_src ( $attachment_id, 'thumbnail' );
			if ( $get_image ) {
				$icon = $get_image[0];
			} 
		}
		return $icon;
	}
	
	add_filter( 'wp_mime_type_icon', 'acf_change_icon_on_files', 10, 3 );

How to use WP_Query to display custom post type

As a developer new to WordPress, you may be asking yourself “How do I display a list of posts of a custom post type on my website?” Whether you are wanting to just display the post title and a link back to the post or present a wide range of dynamic content such as custom fields, images, etc, the powerful WP_Query class makes fetching and outputting your posts a breeze.

WP_Query is a class that accepts parameters to request and fetch posts you specify. The example below allows for us to set a list of parameters, fetch the posts matching those parameters and displays the title and excerpt of the post for the visitor. Taking a look at the example below, we first set up a variable that contains an array of the parameters we want to pass to WP_Query. We will want to set the ‘post_type’ parameter to the slug of the custom post type we’d like to query.  Setting the ‘post_status’ to published will ensure the requested posts are published and not in a ‘draft’ state. We then want to set the number of posts we’d like to fetch and return. Last for the parameters would be ‘orderby’ and ‘order’, the first ‘orderby’ orders the posts by title, the ‘order’ orders all post ascending by the title or ‘orderby’ parameter.

There are many parameters you can use to customize the requested posts with WP_Query. Take a look at the class reference WordPress Codex here to learn more about the different types of parameters you can use in WP_Query.

After we have completed setting up the parameters we will then pass them into the WP_Query class and set the result to a variable. We will then go into our classic WordPress while loop to cycle through the resulting posts and display the title using the_title();.

WP_QUERY CUSTOM POST TYPE EXAMPLE

THE FIRST EXAMPLE

<?php
/**
* Setup query to show the ‘services’ post type with ‘8’ posts.
* Output is title with excerpt.
*/
   $args = array(  
       'post_type' => 'services',
       'post_status' => 'publish',
       'posts_per_page' => 8,
       ‘orderby’ => ‘title’,
       ‘order’ => ‘ASC’,
   );

   $loop = new WP_Query( $args );
     
   while ( $loop->have_posts() ) : $loop->the_post();
       print the_title();
       the_excerpt();
   endwhile;

   wp_reset_postdata();
?>

THE OUTPUT

If we were to run this query within our WordPress template the output may look as follows:

This is the title of the article
This is the example excerpt from the article.. Read More

THE SECOND EXAMPLE:

<?php
/**
 * Setup query to show the ‘services’ post type with all posts filtered by ‘home’ category.
 * Output is linked title with featured image and excerpt.
 */
   
    $args = array(  
        'post_type' => 'services',
        'post_status' => 'publish',
        'posts_per_page' => -1, 
        ‘orderby’ => ‘title’, 
        ‘order’ => ‘ASC’,
        ‘cat’ => ‘home’,
    );

    $loop = new WP_Query( $args ); 
        
    while ( $loop->have_posts() ) : $loop->the_post(); 
        $featured_img = wp_get_attachment_image_src( $post->ID );
        print the_title(); 
        if ( $feature_img ) {
          <img src=”echo $featured_img[‘url’]” width=”echo $featured_img[‘width’]” height=”echo $featured_img[‘height’]” />
        }
        the_excerpt(); 
    endwhile;

    wp_reset_postdata(); 
?>

 

PARAMETERS

There are many different parameters we can pass into our WP_Query to filter our results even further. Some other examples:

  • cat – filters on a specific category
  • tag – filters on a specific tag
  • tax_query – filters on specific taxonomy
  • s – search keyword

TEMPLATE TAGS

Within our custom post type loop, there are many Template Tags you can use inside the loop to output information dynamically. Some examples of other template tags you can use inside your loop:

Now that you understand the basics of WP_Query, requesting and fetching your custom post type. You can use what you’ve learned to develop your own your custom post type templates and display the custom posts easily to visitors.

Dismiss keyboard in multiline TextInput in React native

By doing below code , you keyboard show return key you wanted for example "done", "go" and also dismiss the keyboard when you press return key in my case done key while using multi line seamlessly.

import {Textinput, ScrollView, Keyboard} from 'react-native';

// ...code 

 <TextInput
     keyboardType="default"
     returnKeyType="done"
     multiline={true}
     blurOnSubmit={true}
     onSubmitEditing={()=>{Keyboard.dismiss()}}
  />

Customize your splash screen with Expo for your React Native app

Expo makes it very easy to set a custom splash screen for your app.

If you are already working on your React Native project with Expo, then skip to step 2 of this tutorial.

1 — Set up an Expo roject

Make sure Expo is installed in your local machine by running the following in the terminal.

expo --version

If not then install Expo globally using the command line.

npm install -g expo-cli

Choose the default template when prompted.

Now start a new expo project with an app name of your choice.

expo init appName

2 — Make the changes inside your project

Open the app in your favorite editor. The folder structure should look like the below.

Expo application folder structure (default template).

The actual splash screen is located in the assets folder under the name splash.png.

The splash.png image is served using the app.json file. Open this file.

app.json from the Expo app.

The splash object contains info about the location of the image, its resizeMode, and the backgroundColor. Here is a closer look at this object.

"splash": {  "image": "./assets/splash.png",  "resizeMode": "contain",  "backgroundColor": "#ffffff"},

The goal here is to replace the default splash.png with our custom png image. Let us have a look at the below images.

Default splash screen vs custom splash screen.

The one at the left is the splash screen provided by Expo. It has a white background with a delimiter as the logo icon. To the right side is our custom image with a custom logo icon and a custom background color.

Remark

I used Adobe XD to make my custom image.png. Feel free to use the tool of your choice as this matter is out of the scope of this tutorial.

Give the name splash.png to your custom image and paste it into the assets directory.

When prompt if you want to replace the existing image say yes.

Now go to app.json and replace the backgroundColor with the same color you used for the custom background color on your splash.png.

"splash": {  "image": "./assets/splash.png",  "resizeMode": "contain",  "backgroundColor": "#aa73b7"},

3 — Delete the cache from the simulator

Run your Expo app in the simulator.

expo start --ios# orexpo start --android

The Expo Dev Tool will automatically get opened in the browser.

If you go to the simulator and refresh the app, you still will see the default Expo splash screen. Why is that?

In order to see the changes, we must clear the cache from the simulator.

To clear the cache, you must follow these 3 steps (video provided):

  • Delete the Expo app from your simulator
  • Run the simulator again from the Expo Dev Tools in the browser.
  • Refresh the app and you should see the new splash screen.

Offline image caching in React Native and Expo

I recently worked on an app which required content stored online, but available offline. This content (loaded from a json file) included links to images, so I needed an easy way to show the online version if available, or else have a fallback to a previously downloaded image.

I decided the best option was to download the image and display the local version regardless. This let me put all the image checking stuff outside the render.

First up was to set up the basic structure. I put this in an external component to make it easier to reference. The states were to allow for possible issues: loading while the image download is attempted, failed for if the url is no good, and then imageuri, and width and height for once the image is loaded.

import React, { Component } from 'react'
import { View, Image, ActivityIndicator, Dimensions, Platform } from 'react-native'
import { FileSystem } from 'expo'class CachedImage extends Component {
  state = { 
    loading: true, 
    failed: false,
    imguri: '', 
    width: 300, 
    height: 300
  }render() {
    {
      if (this.state.loading) {
        // while the image is being checked and downloading
        return();
      }
    }
    { 
      if (this.state.failed) {
        // if the image url has an issue
        return();
      }
    }
    // otherwise display the image
    return();
  }
}
export default CachedImage;

Next I needed to set up the code to download the image in componentDidMount(). There were a few possible image extensions, but I also had to deal with the json file sending non-image references, so I checked the extensions first, and if it wasn’t one of the expected ones, set a state of failed to true.

async componentDidMount() {
   const extension = this.props.source.slice((this.props.source.lastIndexOf(".") - 1 >>> 0) + 2)
  if ((extension.toLowerCase() !== 'jpg') && (extension.toLowerCase() !== 'png') && (extension.toLowerCase() !== 'gif')) {
    this.setState({ loading: false, failed: true })
  }

Following this was the code to download the file, save it to the cacheDirectory and then load it with a function. this.props.source and this.props.title were fed into the CachedImage function. I pulled the title from the external image filename so I could track it properly as the json data was updated with new images and the like.

await FileSystem.downloadAsync(
    this.props.source,
    `${FileSystem.cacheDirectory + this.props.title}.${ extension }`
  )
   .then(({ uri }) => {
      // load the local image
      this.loadLocal(uri);
   })
   .catch(e => {
      console.log('Image loading error:', e);
      // if the online download fails, load the local version
      this.loadLocal(`${FileSystem.cacheDirectory + this.props.title}.${ extension }`);
   });

Next up is getting the image data and updating the state. I wanted to set the image width to the device width, and then have the relative height since I couldn’t be sure of the dimensions these images were coming in with, which meant waiting on the Image.getSize function.

loadLocal(uri) {  Image.getSize(uri, (width, height) => {
    // once we have the original image dimensions, set the state to the relative ones
    this.setState({ imguri: uri, loading: false, width: Dimensions.get('window').width, height: (height/width)*Dimensions.get('window').width });
  }, (e) => { 
    // As always include an error fallback
    console.log('getSize error:', e);
    this.setState({ loading: false, failed: true })
  })
}

Finally I just needed to update the render functions to reflect the states. I included a style prop so I could override the sizes, set the resizeMode etc. if needed.

  render() {
    const { style } = this.props
    {
      if (this.state.loading) {
        // while the image is being checked and downloading
        return(
          <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
            <ActivityIndicator
              color='#42C2F3'
              size='large'
            />
          </View>
        );
      }
    }
    { 
      if (this.state.failed) {
        // if the image url has an issue
        return( <View></View> );
      }
    }
    // otherwise display the image
    return(
      <View style={{ width: this.state.width, height: this.state.height }}>
        <Image
          style={[{ width: this.state.width, height: this.state.height }, style ]}
          source={{ uri: this.state.imguri }}
        />
      </View>
    );
  }

Final Gotcha

For whatever reason Android attempts to show the local image before it’s finished downloading. To counter this I ended up just using the online version on that device. Loading it in later while offline is fine, it just triggers that then a little too soon on the initial load.

.then(({ uri }) => {
  this.loadLocal(Platform.OS === 'ios'? uri : this.props.source);
})

This was definitely cobbling together the quickest solution, and I’m certain it could be cleaner (or sorted out properly) with time, but sometimes deadlines mean if it works, it’s fine.


Enough talk, show me the code!

Here’s the final code:

import React, { Component } from 'react'
import { View, Image, ActivityIndicator, Dimensions, Platform } from 'react-native'
import { FileSystem } from 'expo'class CachedImage extends Component {  state = { 
    loading: true, 
    failed: false,
    imguri: '', 
    width: 300, 
    height: 300
  }async componentDidMount() {
    const extension = this.props.source.slice((this.props.source.lastIndexOf(".") - 1 >>> 0) + 2)
    if ((extension.toLowerCase() !== 'jpg') && (extension.toLowerCase() !== 'png') && (extension.toLowerCase() !== 'gif')) {
      this.setState({ loading: false, failed: true })
    }
    await FileSystem.downloadAsync(
        this.props.source,
    `${FileSystem.cacheDirectory + this.props.title}.${ extension }`
    )
    .then(({ uri }) => {
      this.loadLocal(Platform.OS === 'ios'? uri : this.props.source);
    })
    .catch(e => {
      console.log('Image loading error:', e);
      // if the online download fails, load the local version
      this.loadLocal(`${FileSystem.cacheDirectory + this.props.title}.${ extension }`);
    });
  }  loadLocal(uri) {
    Image.getSize(uri, (width, height) => {
      // once we have the original image dimensions, set the state to the relative ones
      this.setState({ imguri: uri, loading: false, width: Dimensions.get('window').width, height: (height/width)*Dimensions.get('window').width });
    }, (e) => { 
      // As always include an error fallback
      console.log('getSize error:', e);
      this.setState({ loading: false, failed: true })
    })
  }  render() {
    const { style } = this.props
    {
      if (this.state.loading) {
        // while the image is being checked and downloading
        return(
          <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
            <ActivityIndicator
              color='#42C2F3'
              size='large'
            />
          </View>
        );
      }
    }
    { 
      if (this.state.failed) {
        // if the image url has an issue
        return( <View></View> );
      }
    }
    // otherwise display the image
    return(
      <View style={{ width: this.state.width, height: this.state.height }}>
        <Image
          style={[{ width: this.state.width, height: this.state.height }, style ]}
          source={{ uri: this.state.imguri }}
        />
      </View>
    );
  }
}
export default CachedImage;

And to load the image in:

<CachedImage
  source={ 'online image url' }
  title={ 'title for the image' }
  style={ whatever extra styling you want }
/>

Thanks for reading. I hope you find it helpful. Please drop me a line if so, or if you have any advice or suggestions for how I could improve this code.

Simple Redux Persist configuration in React Native / Expo

Intro

I have been working with react-persist in some React-Redux-Electron projects in the past, and today I decided to add redux-persist into my new Expo based react-native app.

I must say that, if well its simple enough, I always get lost in their docs. And last year I realized that they do give you more features than they write in their page (I remember to have to dive into their code because I had to “hack” the way the information was stored in an Electron based program, and it was a pain to realize there were hardly info about some of the nice things they let you do).

Anway, I am writing this entry a bit as a reference for me but just in case some other might find it useful.

Redux and Persistence

First a bit of memo. Redux is a javascript library that you can use to store the state of any javascript based webapp you migth want to do. Some people I know were mislead into believing that redux is a part of React, but it is not. However, React and Redux interact very well. As I started using Redux in a non React environment, when I started using it in Redux, I realized how convenient were some of the features that it bring to the table (specifically relating with state subscriptions).

However, Redux doesn’t have any persistence method. When you close the app/website, the info is lost. Luckily you can find things like redux-persist, that let you add a persistence layer to your Redux state.

Redux Persist

Redux Persist is quite a convenient tool, because after it has been configured, it takes charge to all the persistence needs you might have. It gets the redux data and persist it periodically (by default it updates the persistence every time you change any of the reducers, however you can configure the way this persistence is done, debounce it, although this goes beyond to this blog entry objectives).

some concepts:

  • persist: save the store information so it can be recovered in other moment
  • rehydrate: recover the information stored so it can be upated in the redux.

Redux Persist in React-Native

There are some stuff to take in account before adding Redux Persist to your project:

  • You can persist all your reducers or to select specific ones that will be persisted
  • It does not persist by itself. You need to define an Storage Engine that will interact with something (cookie, db, etc) in order to keep the memory. Here we will use AsyncStorage
  • In the case of React and React-Native, you need to add the<PersistGate> component in order to use it properly.

Note: Because in their documentation they only define “React” where they were defining the PersistGate component, it confused me and I didn’t know if I had to add such component into my React-Native App.

Reducer original configuration

In my project I have the Redux configuration in app/store/index.js:

import { createStore }                  from "redux";
import { persistStore, persistReducer } from "redux-persist";import { AsyncStorage }                        from 'react-native'
import {rootReducer}                    from './reducer/index'import storeConstants                          from './constants'const store = createStore(rootReducer, storeConstants.DEF_STORE); const getStore = () => store;
const getState = () => {
    return store.getState();
};export { getStore,    getState,};
export default { getStore,    getState,}

Simple enough. As you can see I defined the rootReducer in another file (app/store/reducer/index).

import { combineReducers }   from 'redux';
import appStateReducer       from './appState/index';const rootReducer = combineReducers({
    appState: appStateReducer
});export {rootReducer};export default {rootReducer}

Also a simple rootReducer. It might grow in the future though.

Finally, App.js is as follows

import React         from 'react';
import {Provider}    from 'react-redux';
import {View}        from 'react-native';
import CConversor    from './app/index.js'
import {getStore}    from './app/store';export default class App extends React.Component {
    render() {
        const myStore = getStore();        
        return (
            <Provider store={ myStore }>                       
                <CConversor/>
            </Provider>        
        );    
    }
}

Modifying Redux Config

So first of all, we need to import the new stuff we will use:

import { persistStore, persistReducer } from "redux-persist";
import { AsyncStorage }                 from 'react-native'
  • persistReducer will be used in our rootReducer to define it as something we want to persist. It is possible to go inside the rootReducer and use persistReducer in some and leave others whitout persistence, but in this blog we will persist all the rootReducer
  • persistStore will be used in the store, after being created, and it will define that the store will use Redux-Persist
  • AsyncStorage is the Engine we are using now. In this case is the react-native default one. There are others, like the expo specific redux-persist-expo-securestore, that add a security layer into the persistence, but I don’t need it for my current project.

The next step is to create a configuration object for persistReducer.

const persistConfig = {
    key: "root",
    storage: AsyncStorage
};

persistConfig can have several keys:

  • key: this value will define what will be the key that we will use as identifier to save the persisted information. Once created must be the same always (if you change the name after there was a persistence, redux-persist won’t find any information and it will load the default values)
  • storage: The engine we will use for persistence.

Even if we don’t use them here, there exists other values that you can add to the config object. Some examples are:

  • blacklist: list of reducers names that will be ignored when the persistence will be done
  • whitelist: list of reducers names that will be used when the persistence will be done. Other reducers will be ignored
  • transforms: custom the way we persist and rehydrate the data
  • stateReconciler: the way that the information will be merged when its being rehydrated.

Next, we need to create the Store and the Persistor

const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(persistedReducer,
                          storeConstants.DEF_STORE);
const persistor = persistStore(store);

In here we have used the persistConf already defined and the rootReducer in persistReducer. This will make a new reducer that we have used in the creation of the store.

Then the persistor is made using persistStore with the store we have created. The persistor will be used when defining the PersistGate in react.

So, after all this modifications, app/store.index.js looks as follows:

import { createStore } from "redux";
import { persistStore, persistReducer } from "redux-persist";
import { AsyncStorage } from 'react-native'
import {rootReducer}   from './reducer/index';
import storeConstants  from './constants'const persistConfig = {
    key: "root",    
    storage: AsyncStorage
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(persistedReducer,                 
                          storeConstants.DEF_STORE);
const persistor = persistStore(store);const getPersistor = () => persistor;
const getStore = () => store;
const getState = () => {
    return store.getState();
};export {
    getStore,
    getState,
    getPersistor
};export default {
    getStore,
    getState,
    getPersistor
}

In my project I use getters to access to the store and the persistors. I also added a getState shortcut, but, as I am using a React-Native-Redux environment, I tend to avoid using it to access to the state unless it is really necesary.

Modifying React

The last step to add the persistence layer to the project is to modify the App.js. In this ile we should add a special Component from redux-persist called PersistGate. This component will make sure that the store is rehydrated before react can access to it.

import React           from 'react';
import {Provider}      from 'react-redux';
import {View, ActivityIndicator} from 'react-native';
import CConversor      from './app/index.js'import {getStore, getPersistor}          from './app/store';
import { PersistGate } from 'redux-persist/integration/react'export default class App extends React.Component {
    renderLoading = () => {
        return (
            <View>                
                <ActivityIndicator size={"large"} />
            </View>        
        );    
    };    render() {
        const myStore = getStore();  
        const myPersistor = getPersistor();        return (
            <Provider store={ myStore }>                
                <PersistGate 
                    persistor={myPersistor} 
                    loading={this.renderLoading()}
                >
                   <CConversor/>                
                </PersistGate>            
          </Provider>        
        );
    }
}

Here we can see that PersistGate must be inside the Provider, so it can access to the store. Also, it has to have as a prop the persistor we made out of the store. PersistGate accepts an extra prop called “loading”, in where you can add a React component to show that the sistem is loading and is not ready yet.

Setting up a Cluster With Proxmox

Clustering is a technique to group together multiple machines or devices providing common parameters, such as storage, over all machines in the cluster. Proxmox can be clustered to provide a common interface to control all machines in the cluster.

Automatic failover of machines can also be achieved with a Proxmox cluster however this requires significant setup and it not available out of the box. I will discuss VM failover in a future blog post.

Create a new Cluster

To join one or more nodes to a Proxmox cluster we must first create a cluster on one of the machines. Run the below command on your first Proxmox node and replace [CLUSTER_NAME] with a name for your cluster.

pvecm create [CLUSTER_NAME]

Check the status of a Cluster

Once a cluster is created, you can check the status using the status command.

pvecm status

You can use the nodes command to view all nodes in the cluster.

pvecm nodes

Add a new Node to the Cluster

Adding a new node to a Proxmox cluster is easy and only requires the hostname or IP address of the primary cluster node. It is highly recommended to use a hostname or DNS entry over an IP address to shield any IP address changes.

On a non-clustered node, log in and run the pvecm add command and specify the IP address or hostname. You can use the IP address or hostname of any already clustered Proxmox node.

pvecm add [HOSTNAME_OF_CLUSTER]

You must not have any virtual machines/ OpenVZ containers running on the node which you are adding to the cluster. All nodes should also be running the same component versions of Proxmox to ensure compatibility.

Fix package com.android.annotations does not exist

Here is how to automatically fix all android to androidx issues for React Native.
1. Please check that you have NPX installed and if not, install it with:
npm install -g npx

2. Add the following two flags to true in your gradle.properties file at ProjectFolder/android/gradle.properties

android.useAndroidX=true
android.enableJetifier=true

3. Execute these lines:

npm install --save-dev jetifier
npx jetify
npx react-native run-android

In your package.json add the following to scripts

  "postinstall" : "npx jetify"

Thats it, you are set, for more info, please go to https://github.com/mikehardy/jetifier