React Native Publishing an Android App

React Native + Redux: Why You Should Be Using Redux-Persist To Save Your Redux Store From Disappearing

Notes:

  • This guide assumes you know React Native and Redux.

1. What Is Redux-Persist?

Say you’re using your app and at some point, you decide to clear out all of your apps because you have too many open. You then click on your app icon and it launches, but you’re now prompted with a Login screen.

You didn’t press log out, so why did you get logged out? It’s because the state isn’t being saved when the app is closed. Redux-Persist saves the Redux Store when the app is closed or refreshed in Expo.

2. How Do We Use Redux Persist?

  • redux (Global Store) (NPM Link)
  • react-redux (For React/React Native) (NPM Link)
  • redux-persist (Persists Redux Global Store) (NPM Link)
  • redux-logger (Logs Redux State for when developing) (NPM Link)
npm install redux react-redux redux-persist --save
npm install redux-logger --save-dev

3. Example App + Code

Github Repo: https://github.com/pavlealeksic/redux-persist-demo

The App will have two ReducersauthReducer and counterReducer. In our store.js file, we can Blacklist or Whitelist reducers to persist data from only specific reducers. In our case, we are only going to persist the data from the authReducer.

To test that Redux Persist is working in our app, we can click the login button (Status should change to true) and increase the counter to 5. When we refresh our app, the Logged In status should remain true (Whitelisted) and the counter should be reset to 0 (Blacklisted).

React Native + Redux: Why You Should Be Using Redux-Persist To Save Your Redux Store From Disappearing
App Screenshot

This example will be using 8 files:

  1. App.js (React Native App)
  2. Counter.js (Counter Screen)
  3. store.js (Redux Store)
  4. index.js (Redux Root Reducer)
  5. authActions.js (Authentication Actions)
  6. counterActions.js (Counter Actions)
  7. authReducer.js (Redux Auth Reducer)
  8. counterReducer.js (Redux Counter Reducer)

App.js

// Imports: Dependencies
import React from 'react';
import { PersistGate } from 'redux-persist/integration/react';
import { Provider } from 'react-redux';// Imports: Screens
import Counter from './screens/Counter';// Imports: Redux Persist Persister
import { store, persistor } from './store/store';// React Native: App
export default function App() {
  return (
    // Redux: Global Store
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        <Counter />
      </PersistGate>
    </Provider>
  );
};

Counter.js

// Imports: Dependencies
import React, { Component } from 'react';
import { Button, Dimensions, SafeAreaView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import { connect } from 'react-redux';// Screen Dimensions
const { height, width } = Dimensions.get('window');// Screen: Counter
class Counter extends React.Component {
  render() {
    return (
      <SafeAreaView style={styles.container}>
        <View style={styles.loggedInContainer}>
          <Text style={styles.loggedInText}>Logged In: </Text>
          <Text style={styles.loggedInText}>{`${this.props.loggedIn}`}</Text>          <Button
            title="Login"
            onPress={this.props.loggedIn === false ? () => this.props.reduxLogin(true) : () => this.props.reduxLogin(false)}
            style={styles.loginButton}
          />
        </View>        <Text style={styles.counterTitle}>Counter</Text>        <View style={styles.counterContainer}>
          <TouchableOpacity onPress={() => this.props.reduxIncreaseCounter()}>
            <Text style={styles.buttonText}>+</Text>
          </TouchableOpacity>          <Text style={styles.counterText}>{this.props.counter}</Text          <TouchableOpacity onPress={() => this.props.reduxDecreaseCounter()}>
            <Text style={styles.buttonText}>-</Text>
          </TouchableOpacity>
        </View>
      </SafeAreaView>
    )
  }
}// Styles
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
   },
   loggedInContainer: {
     display: 'flex',
     flexDirection: 'column',
     justifyContent: 'center',
     alignItems: 'center',
     marginBottom: 40,
   },
   loginButton: {
     marginTop: 20,
     paddingTop: 20,
   },
   counterContainer: {
     display: 'flex',
     flexDirection: 'row',
     justifyContent: 'center',
     alignItems: 'center',
   },
   loggedInText: {
     fontFamily: 'System',
     fontSize: 17,
     fontWeight: '400',
     color: '#000',
   },
   counterTitle: {
     fontFamily: 'System',
     fontSize: 32,
     fontWeight: '700',
     color: '#000',
   },
   counterText: {
     fontFamily: 'System',
     fontSize: 36,
     fontWeight: '400',
     color: '#000',
   },
   buttonText: {
     fontFamily: 'System',
     fontSize: 50,
     fontWeight: '300',
     color: '#007AFF',
     marginLeft: 40,
     marginRight: 40,
   },
});// Map State To Props (Redux Store Passes State To Component)
const mapStateToProps = (state) => {
  // Redux Store --> Component
  return {
    counter: state.counterReducer.counter,
    loggedIn: state.authReducer.loggedIn,
  };
};// Map Dispatch To Props (Dispatch Actions To Reducers. Reducers Then Modify The Data And Assign It To Your Props)
const mapDispatchToProps = (dispatch) => {
  // Action
  return {
    // Increase Counter
    reduxIncreaseCounter: () => dispatch(increaseCounter()),
    // Decrease Counter
    reduxDecreaseCounter: () => dispatch(decreaseCounter()),
    // Login
    reduxLogin: (trueFalse) => dispatch(login(trueFalse)),
  };
};// Exports
export default connect(mapStateToProps, mapDispatchToProps)(Counter);

store.js

// Imports: Dependencies
import AsyncStorage from '@react-native-community/async-storage';
import { createStore, applyMiddleware } from 'redux';
import { createLogger } from 'redux-logger';
import { persistStore, persistReducer } from 'redux-persist';// Imports: Redux
import rootReducer from '../reducers/index';// Middleware: Redux Persist Config
const persistConfig = {
  // Root
  key: 'root',
  // Storage Method (React Native)
  storage: AsyncStorage,
  // Whitelist (Save Specific Reducers)
  whitelist: [
    'authReducer',
  ],
  // Blacklist (Don't Save Specific Reducers)
  blacklist: [
    'counterReducer',
  ],
};// Middleware: Redux Persist Persisted Reducer
const persistedReducer = persistReducer(persistConfig, rootReducer);// Redux: Store
const store = createStore(
  persistedReducer,
  applyMiddleware(
    createLogger(),
  ),
);// Middleware: Redux Persist Persister
let persistor = persistStore(store);// Exports
export {
  store,
  persistor,
};

index.js

// Imports: Dependencies
import { combineReducers } from 'redux';// Imports: Reducers
import authReducer from './authReducer';
import counterReducer from './counterReducer';

// Redux: Root Reducer
const rootReducer = combineReducers({
  authReducer: authReducer,
  counterReducer: counterReducer,
});// Exports
export default rootReducer;

authActions.js

// Login
export const login = (trueFalse) => ({
  type: 'LOGIN',
  trueFalse: trueFalse,
});

counterActions.js

// Increase Counter
export const increaseCounter = () => ({
  type: 'INCREASE_COUNTER',
});// Decrease Counter
export const decreaseCounter = () => ({
  type: 'DECREASE_COUNTER',
});

authReducer.js

// Initial State
const initialState = {
  loggedIn: false,
};// Reducers (Modifies The State And Returns A New State)
const authReducer = (state = initialState, action) => {
  switch (action.type) {    // Logged In
    case 'LOGIN': {
      return {
        // State
        ...state,
        // Redux Store
        email: action.trueFalse,
      }
    }    // Default
    default: {
      return state;
    }
  }
};// Exports
export default authReducer;

counterReducer.js

// Initial State
const initialState = {
  counter: 0,
};// Reducers (Modifies The State And Returns A New State)
const counterReducer = (state = initialState, action) => {
  switch (action.type) {// Increase Counter
    case 'INCREASE_COUNTER': {
      return {
        // State
        ...state,
        // Redux Store
        counter: state.counter + 1,
      }
    }// Decrease Counter
    case 'DECREASE_COUNTER': {
      return {
        // State
        ...state,
        // Redux Store
        counter: state.counter - 1,
      }
    }    // Default
    default: {
      return state;
    }
  }
};// Exports
export default counterReducer;

Keep Persisting

And that’s it! Redux Persist is now working in your app and your Redux Store can now be saved on refresh.

No one’s perfect. If you’ve found any errors, want to suggest enhancements, or expand on a topic, please feel free to send me a message. I will be sure to include any enhancements or correct any issues.

React Native Publishing an Android App

Optimization of a React Native App

I’ve worked for a year on optimization of one of the largest mobile apps performance. Here are some things I wish I knew before starting.

0. Find Your Monsters

A common mistake of optimization is focusing on everything all at once. Get your hands on a slow android phone, install your app and do a complete test run and identify your highway functionality — Ask yourself what does your users do most? which paths is performance a crucial part of the experience?

  • Try removing all animations — how does your app run?
  • Try rendering components with data not being fetched — is it still slow?
  • Try removing a component all together — is the rendering time significantly improving?

Figure out the exact way things are rendered, when is data fetched, when is a new component rendered into the tree — Once you know what is causing the slow downs in your app and it will be much easier to fix.

1. Measure, Measure and Measure Again

Every time you do an optimization, test and measure your change — how do you do that? Use a stop watch, use react-native-debugger or whole third other tool — there’s no silver bullet when it comes to react native performance, cases vary a lot so measuring before and after reliably is absolutely crucial.

I’ve written a longer article about this earlier if your interested 🎉

2. Visualize your bundle

react-native-bundle-visualizer is a great tool for investigating your bundle size. I’ve had a lot of ‘aha’-moments using as i finally had the full picture of the app. As the name implies this will build your app-bundle, help you visualize exactly how big your bundle is and how its size spread on different components and node modules. Is there something here that stands out? Do you really need moment taking up 6% of your apps bundle size?

3. Load Only What You Need When You Need It

This is one of the biggest improvements you can do to start up time. It’s one of those things that are a lot more complex than it should be in react native but its definitely worth it. On my old project our startup time went down from 14 to 4 seconds on the slowest android device we could find.

Facebooks own react native post on performance is one of the best resources on this.

You can see this effect very much on apps like facebook or instagram, they load the bare minimum, then progressively load more and more. This means data fetching and heavy components, the devices are incredibly fast out there, perceived performance is all about loading the most important stuff first. Here’s a solid guide for it.

4. Optimize and standardize assets

Optimizing assets in your React Native apps provides an easy way to achieve big performance gains with little effort.

Why? PNG’s definitely aren’t created equal, especially if you’re working with multiple designers who use various export settings when creating assets.

You can use software like ImageOptim — or automate the whole process doing something like this.

5. Embrace perceived performance

Performance isn’t only execution time, it’s the feeling of a fast app experience you want in the end. Perceived performance is all the tricks you can do to make it seem like your app is running faster and better. Things like letting your splash screen stay until all your javascript is loaded or using react-native-shimmer to let your user see where the content as its loaded.

6. Utilize as much open source as possible and keep them up to date

One of the bitter sweet things i realised developing at a major IT-company is that we could almost never outperform the big open source projects out there. There are thousands of contributors and their range of devices and knowledge between the developers are often far greater than what your company or team has to offer.

The focus of the React Native community has been on app performance in general the last 2 years, especially the react native core team has made some big steps in 0.59 (New JSC) and 0.60.2 (Hermes introduced!).

7. Rewrite Your Animations

Nothing screams “slow app” like laggy animations to the user. Animations can also be a huge performance hog on the JS-thread, rewriting these to use the native thread instead can make a huge difference. Almost every animation is possible using the limited subset of Animated with useNativeDriver:true set — its all about being clever with the implementation.

8. Avoid Micro Optimizations Until Later

They’re often not the real culprit. Focus on the bigger picture before rewriting things unnecessary inline functions etc.

React Native Publishing an Android App

React Native Hooks: To-Do App with CRUD Operations and AsyncStorage

Recently React introduced Hooks, and I was not at all in favour of functional components. I was so used to the class component that this change was not one I was looking forward too. Also, the latest version update was also a significant change. So I tried my hands with both at the same time.

Here is what I came up with. This is basically a trial of making a regular to-do app using React Hooks. So if you are a pro and are looking for something different, this blog is not for you.

Definitions

1. Hooks: Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class. React provides a few built-in Hooks, like useState and useEffect.

2. useState: It returns a pair of values: the current state and a function that updates it. This is why we write const [count, setCount] = useState(). This is similar to this.state.count and this.setState in a class, except you get them in a pair.

Examples how we can initialise states using useStateconst [count, setCount] = useState(0)      // Number
const [name, setName] = useState('James')  // String
const [data, setData] = useState([])       // Array

3. useEffect: By using this Hook, you tell React that your component needs to do something after rendering. Instead of thinking in terms of mounting and updating, you might find it easier to think that effects happen after render. React guarantees the DOM has been updated by the time it runs the effects.

By default, it runs both after the first render  after every update. It is similar to componentDidMount and componentDidUpdate, just cleaner and logical.

const App = ( ) => {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });
}

Introduction

We are going to create a normal to-do app using AsyncStorage.

Requirements

Create a new app with the latest version of React Native.

react-native init tasks
cd tasks

Now when you have created a new app, it’s time to import a few more components.

yarn add 'react-native-dropdownalert';
// To show alert messagesyarn add 'react-native-vector-icons';
// To use icons in the appyarn add '@react-native-community/asyncstorage';
// To use local database

Code Reference

Now remove everything in the App.js file, and add the following code.

UseState explained

// Initialisation of state:this.state {
name: 'James'
}// Updating state value:this.setState({
 name: 'Jeremy'
});console.log(this.state.name);
output: Jeremy
// Now using hooks we can declare this as const [name,setName] = useState('James');
setName('Jeremy');
console.log(name);output: Jeremy

The same process is used to update a string, number, or an array. Nothing is different than the flow described above.

UseEffect explained

//before hookscomponentDidMount(){
  AsyncStorage.clear();
  retrieveData();  
});// using hooksuseEffect(() => {    
  AsyncStorage.clear();
  retrieveData();  
});

Here I am clearing the AsyncStorage using useEffect and calling the retrieveData function to get the value at the time of rendering of the app. There are many other ways to do this — feel free to read the React documentation for more use cases.

AsyncStorage use case

// Save data to AsyncStoragelet user = {
 name,
 email,
 phone, 
 key: Math.random(),
};const arrData = [user];
setData(arrData);
await AsyncStorage.setItem('user', JSON.stringify(arrData));// Get data from AsyncStorageconst retrieveData = async () => {
 try {
  const valueString = await AsyncStorage.getItem('user');
  const value = JSON.parse(valueString);
  setData(value);
 } 
 catch (error) {
  console.log(error);
 }
};
Move away from MAMP to Laravel Valet

Move away from MAMP to Laravel Valet

These step by step instructions will serve you how to easily transfer everything you need from MAMP to Laravel Valet.

Install Composer

wget https://getcomposer.org/download/1.1.0/composer.phar && chmod +x composer.phar && sudo mv /usr/local/bin/composer && composer self-update

Install Homebrew

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Run brew update just to be sure everything is up to date.

Install WP-CLI

wget https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar && chmod +x wp-cli.phar && sudo mv /usr/local/bin/wp

Install Laravel Valet globally

composer global require laravel/valet

Ensure Composer global bin is in your $PATH

which valet >/dev/null 2>&1 || echo 'export PATH="$HOME/.composer/vendor/bin:$PATH"' >> "$HOME/.bash_profile"

Restart your terminal or source "$HOME/.bash_profile"

This also assumes you are using Terminal/iTerm. If you are using a different shell, eg: zsh, you should know how to update your $PATH accordingly

MAMP

Settings > General Tab:

  • UNCHECK: Start Apache and MySQL on system startup
  • UNCHECK: Start Apache and MySQL when opening MAMP PRO
  • CLICK: Set default MAMP ports
  • Save

MAMP is now running on the default non-standard ports so it won’t be conflicting with our new local services. You can continue to use it to host your databases if you want (instead of Homebrew installed MariaDB or MySQL), but it would require more effort than to switch over.

Export all the things!

Export All Databases from MAMP (optional)

mysqldump -u root -p --all-databases > ~/mysql-all-databases.sql

where -p is -pyourRootPasswordYesThereIsNoSpaceThere (leave as-is for blank password) This will export all databases to a file named mysql-all-databases.sql in your user’s home directory.

Run this to ensure its available brew services

Start up MariaDB

brew services mariadb start

All mysql commands should now be targeting mariadb instead of MAMP’s mysql. New credentials are: username root, password “ (empty). To check this, run which mysql It should now be `/usr/local/bin/mysql`

Import all databases

mysql -u root -p < ~/mysql-all-databases.sql

Run Valet setup

valet install

Congratulations, Valet is ready to go!

Let’s try it out…

From the terminal, navigate to the directory which holds your projects/websites. (Easy way – drag the folder from Finder onto the Terminal icon – a new terminal window will open for that directory)

Run valet park It should say “The ____ directory has been added to your paths”

Now, if your project directory has a folder in called “example”, you can now open your browser and go to http://example.dev and it should magically work!

Until now, the instructions have been totally framework agnostic, as Valet supports many.

Using Valet with WordPress

Note: the previous DB_* credentials in wp-config.php will likely need to be updated for the new database. Make sure you update these to reflect the proper DB_USER -> root and DB_PASSWORD -> “ (empty string).

Creating New Sites in a Single Command

One last thing to install…

wp package install aaemnnosttv/wp-cli-valet-command

Meet your new single command workflow

From that same directory, or any other directory you have “parked”, run:

wp valet new my-project

Congratulations, you now have a fresh new install of WordPress in the my-project directory, available RIGHT NOW in your browser at https://my-project.dev

React Native Publishing an Android App

How to fix com.myapp.MainApplication cannot be cast to com.facebook.react.ReactApplication

To fix this issue, thats happening when using react-native-push-notification package with Expokit, you will need to go into your android/src/main/java/host/exp/exponent and open up the MainApplication.java file. The file needs to be edited and changed, so you avoid any errors with getting push notifications. A sample file looks like this:

package host.exp.exponent;


import com.facebook.react.ReactPackage;

import java.util.Arrays;
import java.util.List;

import expolib_v1.okhttp3.OkHttpClient;

// Needed for `react-native link`
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactApplication;
import com.dieam.reactnativepushnotification.ReactNativePushNotificationPackage;
import com.dooboolab.RNIap.RNIapPackage;

public class MainApplication extends ExpoApplication implements ReactApplication{

  @Override
  public ReactNativeHost getReactNativeHost() {
    return mReactNativeHost;
  }


  public List<ReactPackage> getPackages() {
    return Arrays.<ReactPackage>asList(
            new ReactNativePushNotificationPackage(),
            new RNIapPackage()
    );
  }
  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {

    @Override
    public boolean getUseDeveloperSupport() {
      return BuildConfig.DEBUG;
    }

    @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
              new ReactNativePushNotificationPackage(),
              new RNIapPackage()
      );
    }
  };
  @Override
  public boolean isDebug() {
    return BuildConfig.DEBUG;
  }


  @Override
  public String gcmSenderId() {
    return getString(R.string.gcm_defaultSenderId);
  }

  public static OkHttpClient.Builder okHttpClientBuilder(OkHttpClient.Builder builder) {
    // Customize/override OkHttp client here
    return builder;
  }
}

After making changes so your file looks like this, while adding your own packages in the getPackages() functions, rebuild the app, and push notifications should start working if you changed everything correctly.

React Native Publishing an Android App

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

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

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

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.

React Native Publishing an Android App

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()}}
  />