The Subtle Aspects of React Re-rendering (Part 1): Functions Calls and Lazy Evaluation

Jonathan Boccara
Doctolib
Published in
4 min readMay 12, 2021

Like many web companies we use React at Doctolib, and like all web companies we like a website that renders fast.

The best way to improve performance, in a website or anywhere else, is to avoid doing work. This is exactly what React rendering optimizations are for: rendering just the components that need it, and nothing more.

As a developer, if you understand React re-rendering optimizations, you will:

  • benefit from performance gains on your website,
  • stay away from bugs arising when we get in the way of React,
  • better understand how React works.

In this two-post series on React re-rendering, you will learn some of the subtle aspects of React re-rendering:

Part 1 — Functions calls and lazy evaluation
Part 2 — Dynamic children, memos, and constant functions

All the contents I present in this series on React re-rendering have been taught to us by Romain Pellerin, one of our Principal Engineers at Doctolib.

React destroys and remounts components

Consider the following code:

This code is a React component containing an if statement and two return statements. If user.isLoggedIn is false, then only the first return statement is executed, otherwise only the second return statement is executed.

Note that the first return statement calls the showPasswordInput function, whereas the second return statement calls the showCommentInput function.

Those two functions happen to look a lot like one another:

They both return an <input> html component, with different properties.

It all looks good except that, after logging in, this code displays the user’s password in plain text!

To understand why, observe that both return statements in <Page> share a common structure: they both contain an <h3> that contains a <Layout> that contains an <input>.

Now consider the following scenario:

  • The user is not logged in. The first return statement gets executed, React displays the <input> field, with type="password"
  • The user types their password in, the characters are hidden.
  • The user gets logged in.
  • The component is re-rendered. The second return statement gets executed. As an optimization, React keeps the same <input> field, just changing its type. But it keeps the rest of the parameters, including its value!
  • As a result, the input fields show the password in plain text. 😱

How to fix it

The reason why the problem is happening is that we used directly html components for the <input>s, as opposed to encapsulating them in React components.

An easy way to fix it then is to wrap our <input>s in React components. We start by capitalizing the function names to follow React components' convention. And the term “show” is no longer relevant for a component:

And we make the calling code invoke the components by surrounding them with <>:

Note that even though the <input> parameters were already returned by functions in the first example, this wasn't enough for React to consider them as independent entities. We have to call them with the component syntax for React to consider them as independent components, and to re-render them entirely, including the values they contain.

React has lazy evaluation

Now consider the following code, that calls our Page component:

This App component invokes the showComments function, defined here:

The value returned from showComments is passed as a child to Page, and Page uses its children only in its else block, when user.isLoggedIn is true.

But the fact that we’re calling a function, and not a React component, has the effect that showComments is called regardless of the value of user.isLoggedIn.

When user.loggedIn is false, the code of showComments will therefore have no effect on the display of the page, but it wastes rendering time.

If we transform this function call into the invocation of a React component, like this:

then React only renders this component when it needs it, that is in the second return statement. So if user.isLoggedIn is false in <Page>, the code of ShowComments is not called.

The fact that React delays rendering until knowing whether it is needed is called “lazy” evaluation.

React optimizations

Those two examples allow us to realize that React does more than displaying a components tree. It has optimizations in order to render as little components as possible, thus making the rendering as fast as possible.

However, to benefit from those optimizations and to keep a correct behaviour, we need to play by React rules: defining and calling our entities as React components, and not just with functions calls.

In the next post of the series, we’ll dig into other React optimizations: the cases of Contexts, memos and constant functions.

Stay tuned!

If you want more technical news, follow our journey through our docto-tech-life newsletter.

And if you want to join us in scaling a high traffic website and transforming the healthcare system, we are hiring talented developers to grow our tech and product team in France and Germany, feel free to have a look at the open positions.

--

--