How To Use ES6 Arrow Functions With React Native

   JavaScript

When producing React Native applications, you’ll often need to iterate over data to render components, where you’ll need to pass a function as a property for later execution. Think of a <ListView/> for example. We often implement this component so that users can interact with it. In lots of cases, each Row needs to be tappable, thus we must pass an onPress handler.

The most common pattern you’ll find in examples on the web is leveraging the Function.prototype.bind() method to work around JavaScript’s limitation with execution context being lost when a function reference is passed around for later execution.

In this article, we’ll explore why .bind() may not be the best choice and will explore an alternate means of binding your methods by leveraging the ES6 “arrow” function pattern. Instead of just showing a few code snippets, I’ll provide you with a real working app that you can clone and experiment with.

The example app

In order to demonstrate the value of Arrow functions, I created a very simple application that leverages the Giphy API to render a list of still images via the result of a pre-baked API query. When you tap on an image, we’ll leverage React JS’s state management to update the tapped image, allowing it to load and display the animated GIF instead of the still.

How To Use ES6 Arrow Functions with React Native

From the ES6 perspective, this application is broken down into two custom Components: <GiphyList/> and <TouchableImage/>.

<GiphyList/> is a <ScrollView/> implementation, where a list of objects are iterated over and instances of our custom <TouchableImage/> component are created. We could use <ListView/> here for performance reasons, but for simplicity I chose to use a <ScrollView/>.

As you could imagine, <TouchableImage/> is a wrapper for <TouchableHighlight/>, where an <Image/> is configured as its only child.

How To Use ES6 Arrow Functions with React Native

OK, let’s dive into it.

The GiphyList Implementation

Below is the <GiphyList/> implementation:

The part to focus in on is how we pass our <GiphyList/> onItemPress handler as a property to the <TouchableImage/> instances (line #108). It’s important to think about things in the context of a loop for this use case, so I’ll include a snippet below.

for (; i < length; i++) {
    item = data[i];

    images.push(
        <TouchableImage data={item}
                        key={'img' + i}
                        style={styles.touchable}
                        index={i}
                        animate={animIdx == i}
                        onPress={this.onItemPress.bind(this)}/>
      
    )
}

In the above snippet, we have a for loop, whose job is to iterate over the data we gathered from Giphy’s API and push instances of <TouchableImage/> to an array named images, which will be returned at the end of the enclosing function.

With this pattern, we use this.onPress’ bind function to generate a wrapper function. That wrapper function is the the result of the bind() execution and passed as a property for <TouchableImage/>, known as onItemPress.

In short, Function.prototype.bind is a means of enforcing execution context (this keyword). When you pass a reference to a function as an argument to another function or even as as a JSX property (for simplicity, think argument), the execution context for the referenced function (the proper reference for the this keyword) is lost. What this does essentially is create a wrap this.onPress within a function — for each iteration of the loop.

If you’re new to execution context (or scope as it’s referred to sometimes), this can get overwhelming. To illustrate what Function.prototype.bind is doing from a high level, I’ve modified the above for loop with an old-school way of wrapping functions to enforce execution context.

for (; i < length; i++) {
    item = data[i];

    var me = this,
        boundOnItemPress = function() {
            me.onItemPress.apply(me, arguments);
        };

    images.push(
        <TouchableImage data={item}
                        key={'img' + i}
                        style={styles.touchable}
                        index={i}
                        animate={animIdx == i}
                        onPress={boundOnItemPress}/>
      
    )
}

While the JavaScript representation above is slower than Function.prototype.bind(), it illustrates the fundamental concept: For every iteration of the array a new function is created and passed on as an argument.

(If you’re hungry for the deep details of Function.prototype.bind), check out this function from JavaScriptCore, the JavaScript Engine used by React Native for both Android and iOS. If you’re really feeling ambitious, you can review a similar function for V8, Google’s JavaScript engine via this snapshot.)

OK. So you’re probably wondering, what’s the problem here? Why in the world does this matter? Absolutely fantastic question!

Can one function replace many?

We just learned that Function.prototype.bind creates wrapping functions as a means to force execution context. This works well for one-offs, where you may have a few Touchable* components in your React Native application, but creates a problem for lists of items.

Why is this an issue? Computation cost and memory consumption.

To understand why this is a problem, we must put it in the context of a list of items, such as our <GiphyList/> component, or better yet, a <ListView/>.

For each render of an item to be displayed, leverage of Function.prototype.bind adds to CPU cost, but more importantly memory utilization.

For the simple example that I’ve put together, you’re probably thinking, “Meh, who cares?” But for more complex applications where there may be many lists that must reside in memory, memory consumption is something that you should be on your mind often. An example of this can be found in an application that I’m currently working on.

How To Use ES6 Arrow Functions with React Native

Using the pattern we were just looking at, having hundreds of duplicate functions created via Function.prototype.bind just in time, is not ideal. They are all created because Function.prototype.bind helps fix the JavaScript execution problem.

But what if I told you that there is a better way? A pattern that allows you to have the best of both worlds?

How To Use ES6 Arrow Functions with React Native

Enter Arrow functions

As you’ll see, we can use the ES6 Arrow functions when we define our classes to create one self-bound function that can be passed around as a reference without the constant need for Function.prototype.bind.

To make this work, we need to change two lines in the source code for <GiphyList/>: the method signature of onItemPress and its use.

The current onItemPress definition is:

    onItemPress (index, data) {

And needs to be changed to

    onItemPress = (index, data) => {

Next, we need to modify the implementation in the for loop. So, we change:

    onPress={this.onItemPress.bind(this)}/>

Removing the unnecessary .bind() call:

    onPress={this.onItemPress}/>

And that’s it! Now, for every iteration of our for loop, we don’t have to worry about N+ functions being created and put on standby on the off-chance that they will be executed. We create one self-bound function and it does the trick.

Limitations

This pattern only works well for endpoint methods. In other words, this pattern does not allow you to extend these methods.

Why? Because of how Babel transforms your JavaScript, the method is applied to the instance of our <GiphyList/> class at time of instantiation. To make this picture clearer, I’ve published a partially formatted Babel transform of <GiphyList/> here.

Conclusion

There we have it. The use of Arrow functions within the context of ES6 classes in React Native can help reduce our application’s memory consumption and by extension reduce the CPU time required to iterate over loops to generate the components necessary for our lists.

Thank you for reading!

Has this article helped you? Please share it via social media channels.

Have questions or any suggestions for improvement? Post them below.


Like What You See?

Got any questions?