Thanks! We'll be in touch in the next 12 hours
Oops! Something went wrong while submitting the form.

How To Use Inline Functions In React Applications Efficiently

This blog post explores the performance cost of inline functions in a React application. Before we begin, let's try to understand what inline function means in the context of a React application.

What is an inline function?

Simply put, an inline function is a function that is defined and passed down inside the render method of a React component.

Let's understand this with a basic example of what an inline function might look like in a React application:

CODE: https://gist.github.com/velotiotech/a47654d4658294b660a362c4a3c6b2b0.js

The onClick prop, in the example above, is being passed as an inline function that calls this.setState. The function is defined within the render method, often inline with JSX. In the context of React applications, this is a very popular and widely used pattern.

Let's begin by listing some common patterns and techniques where inline functions are used in a React application:

  • Render prop: A component prop that expects a function as a value. This function must return a JSX element, hence the name. Render prop is a good candidate for inline functions.

CODE: https://gist.github.com/velotiotech/a469c1e1fa68cb5dbb6e025cd685bad2.js

  • DOM event handlers: DOM event handlers often make a call to setState or invoke some effect in the React application such as sending data to an API server.

CODE: https://gist.github.com/velotiotech/4ed0152ac7e5f249f3dbe15855ccfc92.js

  • Custom function or event handlers passed to child: Oftentimes, a child component requires a custom event handler to be passed down as props. Inline function is usually used in this scenario.

CODE: https://gist.github.com/velotiotech/d3d731f5b97cf29685db58897fca0a3d.js

Alternatives to inline function

  • Bind in constructor: One of the most common patterns is to define the function within the class component and then bind context to the function in constructor. We only need to bind the current context if we want to use this keyword inside the handler function.

CODE: https://gist.github.com/velotiotech/ad2a21daf37372fbf03d676c5fc83a3c.js

  • Bind in render: Another common pattern is to bind the context inline when the function is passed down. Eventually, this gets repetitive and hence the first approach is more popular.

CODE: https://gist.github.com/velotiotech/2160d4dbc2c5500128efba30501b3750.js

  • Define as public field:

CODE: https://gist.github.com/velotiotech/915bbb287a2dbdb0a45bcfe87eb8ec2a.js

There are several other approaches that React dev community has come up with, like using a helper method to bind all functions automatically in the constructor.

After understanding inline functions with its examples and also taking a look at a few alternatives, let's see why inline functions are so popular and widely used.

Why use inline function

Inline function definitions are right where they are invoked or passed down. This means inline functions are easier to write, especially when the body of the function is of a few instructions such as calling setState. This works well within loops as well.

For example, when rendering a list and assigning a DOM event handler to each list item, passing down an inline function feels much more intuitive. For the same reason, inline functions also make code more organized and readable.

Inline arrow functions preserve context that means developers can use this without having to worry about current execution context or explicitly bind a context to the function.

CODE: https://gist.github.com/velotiotech/c6e31aea581af93c70ecd3cb6b229032.js

Inline functions make value from parent scope available within the function definition. It results in more intuitive code and developers need to pass down fewer parameters. Let's understand this with an example.

CODE: https://gist.github.com/velotiotech/f4227eb8029021fa9c8cba7dc9e2f178.js

Here, the value of count is readily available to onClick event handlers. This behavior is called closing over.

For these reasons, React developers make use of inline functions heavily. That said, inline function has also been a hot topic of debate because of performance concerns. Let's take a look at a few of these arguments.

Arguments against inline functions

  • A new function is defined every time the render method is called. It results in frequent garbage collection, and hence performance loss.
  • There is an eslint config that advises against using inline function jsx-no-bind. The idea behind this rule is when an inline function is passed down to a child component, React uses reference checks to re-render the component. This can result in child component rendering again and again as a reference to the passed prop value i.e. inline function. In this case, it doesn't match the original one.

<ListItem onClick={() => console.log('click')}></Foo>

Suppose ListItem component implements shouldComponentUpdate method where it checks for onClick prop reference. Since inline functions are created every time a component re-renders, this means that the ListItem component will reference a new function every time, which points to a different location in memory. The comparison checks in shouldComponentUpdate and tells React to re-render ListItem even though the inline function's behavior doesn't change. This results in unnecessary DOM updates and eventually reduces the performance of applications.

Performance concerns revolving around the Function.prototype.bind methods: when not using arrow functions, the inline function being passed down must be bound to a context if using this keyword inside the function. The practice of calling .bind before passing down an inline function raises performance concerns, but it has been fixed. For older browsers, Function.prototype.bind can be supplemented with a polyfill for performance.

Now that we've summarized a few arguments in favor of inline functions and a few arguments against it, let's investigate and see how inline functions really perform.

CODE: https://gist.github.com/velotiotech/d62692f9f806cc8aa1e562437adcbbb7.js

Pre-optimization can often lead to bad code. For instance, let's try to get rid of all the inline function definitions in the component above and move them to the constructor because of performance concerns.

We'd then have to define 3 custom event handlers in the class definition and bind context to all three functions in the constructor.

CODE: https://gist.github.com/velotiotech/59cec6edcf21ddaad944d6155fd5d3d2.js

This would increase the initialization time of the component significantly as opposed to inline function declarations where only one or two functions are defined and used at a time based on the result of condition timeThen > timeNow.

Concerns around render props: A render prop is a method that returns a React element and is used to share state among React components.

Render props are meant to be invoked on each render since they share state between parent components and enclosed React elements. Inline functions are a good candidate for use in render prop and won't cause any performance concern.

CODE: https://gist.github.com/velotiotech/1ca1eea9d2cc3f26243dc8ef4ca8c737.js

Here, the render prop of ListView component returns a label enclosed in a div. Since the enclosed component can never know what the value of the item variable is, it can never be a PureComponent or have a meaningful implementation of shouldComponentUpdate(). This eliminates the concerns around use of inline function in render prop. In fact, promotes it in most cases.

In my experience, inline render props can sometimes be harder to maintain especially when render prop returns a larger more complicated component in terms of code size. In such cases, breaking down the component further or having a separate method that gets passed down as render prop has worked well for me.

Concerns around PureComponents and shouldComponentUpdate(): Pure components and various implementations of shouldComponentUpdate both do a strict type comparison of props and state. These act as performance enhancers by letting React know when or when not to trigger a render based on changes to state and props. Since inline functions are created on every render, when such a method is passed down to a pure component or a component that implements the shouldComponentUpdate method, it can lead to an unnecessary render. This is because of the changed reference of the inline function.

To overcome this, consider skipping checks on all function props in shouldComponentUpdate(). This assumes that inline functions passed to the component are only different in reference and not behavior. If there is a difference in the behavior of the function passed down, it will result in a missed render and eventually lead to bugs in the component's state and effects.

Conclusion

A common rule of thumb is to measure performance of the app and only optimize if needed. Performance impact of inline function, often categorized under micro-optimizations, is always a tradeoff between code readability, performance gain, code organization, etc that must be thought out carefully on a case by case basis and pre-optimization should be avoided.

In this blog post, we observed that inline functions don't necessarily bring in a lot of performance cost. They are widely used because of ease of writing, reading and organizing inline functions, especially when inline function definitions are short and simple.