Building Mobile Apps Using React Native And WordPress

About The Author

Muhammad is a full stack engineer and consultant, with a focus on React and WordPress. He is the Lead Developer of the GatsbyWPThemes.com project. Lately, he … More about Muhammad ↬

Email Newsletter

Weekly tips on front-end & UX.
Trusted by 200,000+ folks.

WordPress can work as an excellent back-end platform for your next native app, especially if it is content-driven or an online shop. In this article, Muhammad Mushin will show you the foundations for building mobile apps with React Native and WordPress. You will learn how to build an e-commerce application for iOS and Android using the WooCommerce platform as your backend. This would be an ideal starting point for anyone willing to get into native cross-platform development.

As web developers, you might have thought that mobile app development calls for a fresh learning curve with another programming language. Perhaps Java and Swift need to be added to your skill set to hit the ground running with both iOS and Android, and that might bog you down.

But this article has you in for a surprise! We will look at building an e-commerce application for iOS and Android using the WooCommerce platform as our backend. This would be an ideal starting point for anyone willing to get into native cross-platform development.

A Brief History Of Cross-Platform Development

It’s 2011, and we see the beginning of hybrid mobile app development. Frameworks like Apache Cordova, PhoneGap, and Ionic Framework slowly emerge. Everything looks good, and web developers are eagerly coding away mobile apps with their existing knowledge.

However, mobile apps still looked like mobile versions of websites. No native designs like Android’s material design or iOS’s flat look. Navigation worked similar to the web and transitions were not buttery smooth. Users were not satisfied with apps built using the hybrid approach and dreamt of the native experience.

Fast forward to March 2015, and React Native appears on the scene. Developers are able to build truly native cross-platform applications using React, a favorite JavaScript library for many developers. They are now easily able to learn a small library on top of what they know with JavaScript. With this knowledge, developers are now targeting the web, iOS and Android.

Furthermore, changes done to the code during development are loaded onto the testing devices almost instantly! This used to take several minutes when we had native development through other approaches. Developers are able to enjoy the instant feedback they used to love with web development.

React developers are more than happy to be able to use existing patterns they have followed into a new platform altogether. In fact, they are targeting two more platforms with what they already know very well.

This is all good for front-end development. But what choices do we have for back-end technology? Do we still have to learn a new language or framework?

The WordPress REST API

In late 2016, WordPress released the much awaited REST API to its core, and opened the doors for solutions with decoupled backends.

So, if you already have a WordPress and WooCommerce website and wish to retain exactly the same offerings and user profiles across your website and native app, this article is for you!

Assumptions Made In This Article

I will walk you through using your WordPress skill to build a mobile app with a WooCommerce store using React Native. The article assumes:

  • You are familiar with the different WordPress APIs, at least at a beginner level.
  • You are familiar with the basics of React.
  • You have a WordPress development server ready. I use Ubuntu with Apache.
  • You have an Android or an iOS device to test with Expo.

What We Will Build In This Tutorial

The project we are going to build through this article is a fashion store app. The app will have the following functionalities:

  • Shop page listing all products,
  • Single product page with details of the selected item,
  • ‘Add to cart’ feature,
  • ‘Show items in cart’ feature,
  • ‘Remove item from cart’ feature.

This article aims to inspire you to use this project as a starting point to build complex mobile apps using React Native.

Note: For the full application, you can visit my project on Github and clone it.

Getting Started With Our Project

We will begin building the app as per the official React Native documentation. Having installed Node on your development environment, open up the command prompt and type in the following command to install the Create React Native App globally.

npm install -g create-react-native-app

Next, we can create our project

create-react-native-app react-native-woocommerce-store

This will create a new React Native project which we can test with Expo.

Next, we will need to install the Expo app on our mobile device which we want to test. It is available for both iOS and Android.

On having installed the Expo app, we can run npm start on our development machine.

cd react-native-woocommerce-store

npm start
Starting a React Native project through the command line via Expo. (Large preview)

After that, you can scan the QR code through the Expo app or enter the given URL in the app’s search bar. This will run the basic ‘Hello World’ app in the mobile. We can now edit App.js to make instant changes to the app running on the phone.

Alternatively, you can run the app on an emulator. But for brevity and accuracy, we will cover running it on an actual device.

Next, let’s install all the required packages for the app using this command:

npm install -s axios react-native-htmlview react-navigation react-redux redux redux-thunk

Setting Up A WordPress Site

Since this article is about creating a React Native app, we will not go into details about creating a WordPress site. Please refer to this article on how to install WordPress on Ubuntu. As WooCommerce REST API requires HTTPS, please make sure it is set up using Let’s Encrypt. Please refer to this article for a how-to guide.

We are not creating a WordPress installation on localhost since we will be running the app on a mobile device, and also since HTTPS is needed.

Once WordPress and HTTPS are successfully set up, we can install the WooCommerce plugin on the site.

Installing the WooCommerce plugin to our WordPress installation. (Large preview)

After installing and activating the plugin, continue with the WooCommerce store setup by following the wizard. After the wizard is complete, click on ‘Return to dashboard.’

You will be greeted by another prompt.

Adding example products to WooCommerce. (Large preview)

Click on ‘Let’s go‘ to 'Add example products'. This will save us the time to create our own products to display in the app.

Constants File

To load our store’s products from the WooCommerce REST API, we need the relevant keys in place inside our app. For this purpose, we can have a constans.js file.

First create a folder called ‘src’ and create subfolders inside as follows:

Create the file ‘Constants.js’ within the constans folder. (Large preview)

Now, let’s generate the keys for WooCommerce. In the WordPress dashboard, navigate to WooCommerce → Settings → API → Keys/Apps and click on ‘Add Key.’

Next create a Read Only key with name React Native. Copy over the Consumer Key and Consumer Secret to the constants.js file as follows:

const Constants = {
   URL: {
wc: 'https://woocommerce-store.on-its-way.com/wp-json/wc/v2/'
   },
   Keys: {
ConsumerKey: 'CONSUMER_KEY_HERE',
ConsumerSecret: 'CONSUMER_SECRET_HERE'
   }
}
export default Constants;

Starting With React Navigation

React Navigation is a community solution to navigating between the different screens and is a standalone library. It allows developers to set up the screens of the React Native app with just a few lines of code.

There are different navigation methods within React Navigation:

  • Stack,
  • Switch,
  • Tabs,
  • Drawer,
  • and more.

For our Application we will use a combination of StackNavigation and DrawerNavigation to navigate between the different screens. StackNavigation is similar to how browser history works on the web. We are using this since it provides an interface for the header and the header navigation icons. It has push and pop similar to stacks in data structures. Push means we add a new screen to the top of the Navigation Stack. Pop removes a screen from the stack.

The code shows that the StackNavigation, in fact, houses the DrawerNavigation within itself. It also takes properties for the header style and header buttons. We are placing the navigation drawer button to the left and the shopping cart button to the right. The drawer button switches the drawer on and off whereas the cart button takes the user to the shopping cart screen.

const StackNavigation = StackNavigator({
 DrawerNavigation: { screen: DrawerNavigation }
}, {
   headerMode: 'float',
   navigationOptions: ({ navigation, screenProps }) => ({
     headerStyle: { backgroundColor: '#4C3E54' },
     headerTintColor: 'white',
     headerLeft: drawerButton(navigation),
     headerRight: cartButton(navigation, screenProps)
   })
 });

const drawerButton = (navigation) => (
 <Text
   style={{ padding: 15, color: 'white' }}
   onPress={() => {
     if (navigation.state.index === 0) {
       navigation.navigate('DrawerOpen')
     } else {
       navigation.navigate('DrawerClose')
     }
   }
   }> (
 <Text style={{ padding: 15, color: 'white' }}
   onPress={() => { navigation.navigate('CartPage') }}
 >
   <EvilIcons name="cart" size={30} />
   {screenProps.cartCount}
 </Text>
);

DrawerNavigation on the other hands provides for the side drawer which will allow us to navigate between Home, Shop, and Cart. The DrawerNavigator lists the different screens that the user can visit, namely Home page, Products page, Product page, and Cart page. It also has a property which will take the Drawer container: the sliding menu which opens up when clicking the hamburger menu.

const DrawerNavigation = DrawerNavigator({
 Home: {
   screen: HomePage,
   navigationOptions: {
     title: "RN WC Store"
   }
 },
 Products: {
   screen: Products,
   navigationOptions: {
     title: "Shop"
   }
 },
 Product: {
   screen: Product,
   navigationOptions: ({ navigation }) => ({
     title: navigation.state.params.product.name
   }),
 },
 CartPage: {
   screen: CartPage,
   navigationOptions: {
     title: "Cart"
   }
 }
}, {
   contentComponent: DrawerContainer
 });
#
Left: The Home page (homepage.js). Right: The open drawer (DrawerContainer.js).

Injecting The Redux Store To App.js

Since we are using Redux in this app, we have to inject the store into our app. We do this with the help of the Provider component.

const store = configureStore();

class App extends React.Component {
 render() {
   return (
     <Provider store={store}>    
       <ConnectedApp />    
     </Provider>    
   )
 }
}

We will then have a ConnectedApp component so that we can have the cart count in the header.

class CA extends React.Component {
 render() {
   const cart = {
     cartCount: this.props.cart.length
   }
   return (
     <StackNavigation screenProps={cart} />
   );
 }
}

function mapStateToProps(state) {
 return {
   cart: state.cart
 };
}

const ConnectedApp = connect(mapStateToProps, null)(CA);

Redux Store, Actions, And Reducers

In Redux, we have three different parts:

  1. Store
    Holds the whole state of your entire application. The only way to change state is to dispatch an action to it.
  2. Actions
    A plain object that represents an intention to change the state.
  3. Reducers
    A function that accepts a state and an action type and returns a new state.

These three components of Redux help us achieve a predictable state for the entire app. For simplicity, we will look at how the products are fetched and saved in the Redux store.

First of all, let’s look at the code for creating the store:

let middleware = [thunk];

export default function configureStore() {
    return createStore(
        RootReducer,
        applyMiddleware(...middleware)
    );
}

Next, the products action is responsible for fetching the products from the remote website.

export function getProducts() {
   return (dispatch) => {
       const url = `${Constants.URL.wc}products?per_page=100&consumer_key=${Constants.Keys.ConsumerKey}&consumer_secret=${Constants.Keys.ConsumerSecret}`

       return axios.get(url).then(response => {
           dispatch({
               type: types.GET_PRODUCTS_SUCCESS,
               products: response.data
           }
       )}).catch(err => {
           console.log(err.error);
       })
   };
}

The products reducer is responsible for returning the payload of data and whether it needs to be modified.

export default function (state = InitialState.products, action) {
    switch (action.type) {
        case types.GET_PRODUCTS_SUCCESS:
            return action.products;
        default:
            return state;
    }
}

Displaying The WooCommerce Shop

The products.js file is our Shop page. It basically displays the list of products from WooCommerce.

class ProductsList extends Component {

 componentDidMount() {
   this.props.ProductAction.getProducts(); 
 }

 _keyExtractor = (item, index) => item.id;

 render() {
   const { navigate } = this.props.navigation;
   const Items = (
     <FlatList contentContainerStyle={styles.list} numColumns={2}
       data={this.props.products || []} 
       keyExtractor={this._keyExtractor}
       renderItem={
         ({ item }) => (
           <TouchableHighlight style={{ width: '50%' }} onPress={() => navigate("Product", { product: item })} underlayColor="white">
             <View style={styles.view} >
               <Image style={styles.image} source={{ uri: item.images[0].src }} />
               <Text style={styles.text}>{item.name}</Text>
             </View>
           </TouchableHighlight>
         )
       }
     />
   );
   return (
     <ScrollView>
       {this.props.products.length ? Items :
         <View style={{ alignItems: 'center', justifyContent: 'center' }}>
           <Image style={styles.loader} source={LoadingAnimation} />
         </View>
       }
     </ScrollView>
   );
 }
}

this.props.ProductAction.getProducts() and this.props.products are possible because of mapStateToProps and mapDispatchToProps.

Products listing screen. (Large preview)

mapStateToProps And mapDispatchToProps

State is the Redux store and Dispatch is the actions we fire. Both of these will be exposed as props in the component.

function mapStateToProps(state) {
 return {
   products: state.products
 };
}
function mapDispatchToProps(dispatch) {
 return {
   ProductAction: bindActionCreators(ProductAction, dispatch)
 };
}
export default connect(mapStateToProps, mapDispatchToProps)(ProductsList);

Styles

In React, Native styles are generally defined on the same page. It’s similar to CSS, but we use camelCase properties instead of hyphenated properties.

const styles = StyleSheet.create({
 list: {
   flexDirection: 'column'
 },
 view: {
   padding: 10
 },
 loader: {
   width: 200,
   height: 200,
   alignItems: 'center',
   justifyContent: 'center',
 },
 image: {
   width: 150,
   height: 150
 },
 text: {
   textAlign: 'center',
   fontSize: 20,
   padding: 5
 }
});

Single Product Page

This page contains details of a selected product. It shows the user the name, price, and description of the product. It also has the ‘Add to cart’ function.

Single product page. (Large preview)

Cart Page

This screen shows the list of items in the cart. The action has the functions getCart, addToCart, and removeFromCart. The reducer handles the actions likewise. Identification of actions is done through actionTypes — constants which describe the action that are stored in a separate file.

export const GET_PRODUCTS_SUCCESS = 'GET_PRODUCTS_SUCCESS'
export const GET_PRODUCTS_FAILED = 'GET_PRODUCTS_FAILED';

export const GET_CART_SUCCESS = 'GET_CART_SUCCESS';
export const ADD_TO_CART_SUCCESS = 'ADD_TO_CART_SUCCESS';
export const REMOVE_FROM_CART_SUCCESS = 'REMOVE_FROM_CART_SUCCESS';

This is the code for the CartPage component:

class CartPage extends React.Component {

 componentDidMount() {
   this.props.CartAction.getCart();
 }

 _keyExtractor = (item, index) => item.id;

 removeItem(item) {
   this.props.CartAction.removeFromCart(item);
 }

 render() {
   const { cart } = this.props;
   console.log('render cart', cart)

   if (cart && cart.length > 0) {
     const Items = <FlatList contentContainerStyle={styles.list}
       data={cart}
       keyExtractor={this._keyExtractor}
       renderItem={({ item }) =>
         <View style={styles.lineItem} >
           <Image style={styles.image} source={{ uri: item.image }} />
           <Text style={styles.text}>{item.name}</Text>
           <Text style={styles.text}>{item.quantity}</Text>
           <TouchableOpacity style={{ marginLeft: 'auto' }} onPress={() => this.removeItem(item)}><Entypo name="cross" size={30} /></TouchableOpacity>
         </View>
       }
     />;
     return (
       <View style={styles.container}>
         {Items}
       </View>
     )
   } else {
     return (
       <View style={styles.container}>
         <Text>Cart is empty!</Text>
       </View>
     )
   }
 }
}

As you can see, we are using a FlatList to iterate through the cart items. It takes in an array and creates a list of items to be displayed on the screen.

#
Left: The cart page when it has items in it. Right: The cart page when it is empty.

Conclusion

You can configure information about the app such as name and icon in the app.json file. The app can be published after npm installing exp.

To sum up:

  • We now have a decent e-commerce application with React Native;
  • Expo can be used to run the project on a smartphone;
  • Existing backend technologies such as WordPress can be used;
  • Redux can be used for managing the state of the entire app;
  • Web developers, especially React developers can leverage this knowledge to build bigger apps.

For the full application, you can visit my project on Github and clone it. Feel free to fork it and improve it further. As an exercise, you can continue building more features into the project such as:

  • Checkout page,
  • Authentication,
  • Storing the cart data in AsyncStorage so that closing the app does not clear the cart.

Further Reading

Smashing Editorial (da, lf, ra, yk, il, mrn)