Overview

Skill Level: Intermediate

A collection of tips and tricks that I've learned over the years building apps using React Native. The topics covered include smart vs. dumb components, higher-order components, optimization techniques, options for testing and more.

Ingredients

React Native, a framework for building cross-platform mobile applications has been in the spotlight lately. It’s a new web technology built on top of the popular React library and is currently used to build some of the popular mobile apps such as Facebook, Instagram, Airbnb etc. However, there is a familiar misconception that it is impossible to achieve the native look, feel and performance using a cross-platform framework. But React Native is different in that regard. The underlying React architecture can be optimized and tweaked to meet the performance and aesthetic benchmarks, identical to that of a native application. In this recipe, I am going to share some tips and tricks that I’ve learned over the years creating React Native applications.

Step-by-step

  1. Smart vs Dumb Components

    Dividing your app’s logic into two categories is helps you reuse your components better. Smart components, also known as the container components are concerned with the actual working of the UI. It is responsible for making API calls, fetching data, managing the state and so on. They have zero knowledge of how the data is presented to the user. Instead, it transfers the data over to the dumb components as props.

    Dumb components, popularly known as presentational components, are concerned with how the data is presented and the actual appearance of the app. This is where all the HTML and the styling for that particular UI element is rendered. They use the props received in a meaningful way and have no dependencies on any state management libraries. You can bind them with other container components, making them reusable as long as the props match.

  2. Reusable Code Using Higher-Order Components

    A common trend during the development phase is writing lots of components that have almost the same features. Here is an example of two components that share almost identical implementation.

    CommentList.jsx

    class CommentList extends React.Component {
    constructor(props) {
    super(props);
    this.state = {
    // "DataSource" is some global data source
    comments: DataSource.getComments()
    };
    }

    handleChange() {
    // Update component state whenever the data source changes
    this.setState({
    comments: DataSource.getComments()
    });
    }

    render() {
    return (
    <div>
    {this.state.comments.map((comment) => (
    <Comment comment={comment} key={comment.id} />
    ))}
    </div>
    );
    }
    }

     BlogpostList.jsx

    class BlogpostList extends React.Component {
    constructor(props) {
    super(props);
    this.state = {
    // "DataSource" is some global data source
    posts: DataSource.getPosts()
    };
    }

    handleChange() {
    // Update component state whenever the data source changes
    this.setState({
    posts: DataSource.getPosts()
    });
    }

    render() {
    return (
    <div>
    {this.state.posts.map((post) => (
    <BlogPost post={post} key={post.id} />
    ))}
    </div>
    );
    }
    }

    Both the components look identical. Wouldn’t it be awesome if there was an abstract component that would let us share code between the two? That’s what higher-order components are for. A higher-order component is a function that accepts a component as an input and returns an entirely new component.

    const EnhancedComponent = higherOrderComponent(WrappedComponent);

    Inside the returned component, you can add state, abstract away common logic or hijack the render method — anything is possible.

    Apart from HOCs, there are alternate patterns that let you share code between components and render props is popular amongst them. Learning to reuse code using standard react patterns makes the code more readable and maintainable.

  3. Optimize React Native Images And Other Resources

    Images are resources that a modern-day application cannot live without. Images can be a static resource accessed from a temporary local directory or an external resource that needs to be fetched from a backend server. Whatever your requirements are, optimizing React Native images should be given higher priority. Rather than processing them from the client-end and end up crashing your performance and bandwidth benchmarks, you should have them optimized at the server level. With many CDN options that include IBM Cloud for hosting images and videos, you can easily make API calls to upload the images to the server. You can then use the cloud resources to optimize the images for you and return them back using an API.

  4. Use Platform Specific Code and Styles

    Although React Native offers built-in API to write code that works on both iOS and Android, you will always end up writing platform specific things like stylesheets, and callbacks. To organize your code better, you have two options.

    1. Use the Platform module which is part of the React Native Framework
    2. Use platform-specific file extensions.

    I prefer using the Platform module for stylesheets. For instance, you can use the Platform.OS or the Platform.select APIs to automatically detect the platform and apply the right styles. Here a demo that shows you how Platform module could be configured.

    import { Platform, StyleSheet } from 'react-native';

    const styles = StyleSheet.create({
    container: {
    flex: 1,
    ...Platform.select({
    ios: {
    fontFamily: 'Arial',
    },
    android: {
    fontFamily: 'Roboto',
    },
    }),
    },
    });

    You can even describe version-specific styles for Android and iOS to make your app feel natural.

    Choose the second option when your platform-specific styles become more complex. This is because you will be duplicating efforts by maintaining two files, one for iOS and the other for Android. Each file will have an .ios or .android extension.

  5. Test Your Native Application Using Jest

    The React ecosystem has some good libraries for testing your application. I use Jest for unit testing and Calabash for feature testing. Jest is a behavior driven testing library inspired by the Jasmine test framework. Calabash, on the other hand, is a UI acceptance testing framework that lets you run automated functional tests on Android and iOS. It supports the Cucumber library so that you can write your tests in a natural language. The combination of Jest and Calabash should be sufficient to test almost all aspects of your application.

  6. Do not Optimize Unless You Experience an Actual Hitch in Performance

    Under the hood, React has many clever techniques that reduce the number of DOM updates made using their virtual DOM technology. And fortunately, this is all the optimization that you will need most of the time. In my opinion, you should start optimizing your React Native application only when you start experiencing a decline in performance. I am not against the idea of building an optimized application from the start. But if you’re going to spend resources and time on something that’s not part of the equation, what’s the point?

    One of my personal favorite performance scaling technique is tweaking the shouldComponentUpdate lifecycle method. By default, it returns true. But you can instead write some custom logic that decides when your component should update. This will prevent unnecessary re-rendering of the component. Read more about it in the React documentation.

  7. Summary

    This recipe has described some of the common React Native tips and tricks that you might find helpful. What are your thoughts on this recipe? If you have any tips or tricks that you’d like to share, please let me know them through the comments.

Join The Discussion