



At this point, it’s tough to argue that React is one of the most loved libraries on the planet. There is a tremendous amount of interest in React and new developers are swayed into the platform because of its UI-first approach. And while both the library and the entire React ecosystem have matured over the years, there are certain instances where you find yourself asking “what’s the right way to do this, exactly?”

And that’s a fair question to ask — there isn’t always a firm “right” way of doing things. In fact, as you likely already know, sometimes best practices aren’t so great. Some of them can compromise performance, readability and make things unproductive in the long run.

In this article, I’ll describe 5 generally accepted development practices that you can actually avoid when using React. Naturally, I’ll explain why I consider the practice avoidable and suggest alternative approaches that let you accomplish the same thing.

Optimizing React right from the start

The developers at React have put a lot of effort into making React fast and new optimizations are added to the mix after each new update. In my opinion, you shouldn’t spend time optimizing stuff until you see actual performance hits.

Why?

It’s easier to scale React compared to other front-end platforms because you don’t have to rewrite entire modules to make things faster. The usual culprit that causes performance issues is the reconciliation process that React uses to update the virtual DOM.

Let’s have a look at how React handles things under the hood. On each render(), React generates a tree that’s composed of UI elements — the leaf nodes being the actual DOM elements. When the state or props get updated, React needs to generate a new tree with minimal number changes and keep things predictable.

Imagine you have a tree that looks like this:





Imagine that the application receives new data and the following nodes need to be updated:





React usually ends up re-rendering the entire subtree instead of rendering only the relevant nodes like this:





When the state changes at top-order components, all the components below it get re-rendered. That’s the default behavior and it’s okay for a small-sized application. As the application grows, you should consider measuring the actual performance using Chrome Profiling Tools. The tool will give you precise details about the time wasted on unwanted renders. If the numbers are significant, you can then optimize the rendering time by adding a shouldComponentUpdate hook into your component.

The hook gets triggered before the re-rendering process starts and by default, it returns true:

shouldComponentUpdate(nextProps, nextState) { return true; }

When it returns true, React’s diff algorithm takes over and re-renders the entire subtree. You can avoid that by adding comparison logic into shouldComponentUpdate and updating the logic only when the relevant props have changed.

shouldComponentUpdate(nextProps, nextState) { if (this.props.color !== nextProps.color) { return true; } if (this.state.count !== nextState.count) { return true; } return false; }

The component won’t update if any other props/state has changed except color/count.

Apart from this, there are certain non-React optimization tricks that developers usually miss, but they have an impact on the application’s performance.

I’ve listed some of the avoidable habits and the solutions below:

Unoptimized images — If you’re building on dynamic images, you need to consider your options while dealing with images. Images with huge file sizes can give the user an impression that the application is slow. Compress the images before you push them into the server or use a dynamic image manipulation solution instead. I personally like Cloudinary to optimize react images because it has its own react library, but you could also use Amazon S3 or Firebase instead. Uncompressed build files — Gzipping build files (bundle.js) can reduce the file size by a good amount. You will need to make modifications to the webserver configuration. Webpack has a gzip compression plugin known as compression-webpack-plugin. You can use this technique to generate bundle.js.gz during build time.

Server-side rendering for SEO

Although Single Page applications are awesome, there are two issues that are still attributed back to them.

When the application loads initially, there is no cache of JavaScript in the browser. If the application is big, the time taken to initially load the application will also be huge. Since the application is rendered in the client side, the web crawlers that search engines use won’t be able to index the JavaScript generated content. The search engines will see your application to be blank and then rank you poorly.

That’s where the server-side rendering technique comes in handy. In SSR, the JavaScript content is rendered from the server initially. After the initial render, the client-side script takes over and it works like a normal SPA. The complexity and the cost involved in setting up the traditional SSR is higher because you need to use a Node/Express server.

There’s good news if you’re in it for the SEO benefit, Google indexes and crawls the JavaScript content without any trouble. Google actually started to crawl JavaScript material back in 2016 and the algorithm works flawlessly right now.

Here’s an excerpt from the Webmaster blog back in October 2015.

Today, as long as you’re not blocking Googlebot from crawling your JavaScript or CSS files, we are generally able to render and understand your web pages like modern browsers. To reflect this improvement, we recently updated our technical Webmaster Guidelines to recommend against disallowing Googlebot from crawling your site’s CSS or JS files.

If you’re only using server-side rendering because you’re worried about your Google Page Rank, then you don’t need to use SSR. It used to be a thing in the past, but not anymore.

However, if you’re doing it to improve the initial render speed, then you should try an easier implementation of SSR using a library like Next.js. Next saves you time that you would otherwise spend on setting up the Node/Express server.

Inline styles & CSS imports

While working with React, I’ve personally tried different styling ideas to find new ways to introduce styles into React components. The traditional CSS-in-CSS approach that has been around for decades works with React components. All your stylesheets would go into a stylesheets directory and you can then import the required CSS into your component.

However, when you’re working with components, stylesheets don’t make sense anymore. While React encourages you to think of your application in terms of components, stylesheets force you to think of it at the document level.

Various other approaches are being practiced to merge the CSS and the JS code into a single file. The Inline Style is probably the most popular among them.

import React from 'react'; const divStyle = { margin: '40px', border: '5px solid pink' }; const pStyle = { fontSize: '15px', textAlign: 'center' }; const TextBox = () => ( <div style={divStyle}> <p style={pStyle}>Yeah!</p> </div> ); export default TextBox;

You don’t have to import CSS anymore, but you’re sacrificing readability and maintainability. Apart from that, Inline Styles don’t support media queries, pseudo classes and pseudo elements and style fallbacks. Sure, there are hacks that let you do some of them, but it’s just not that convenient.

That’s where CSS-in-JSS comes in handy and Inline Styles are not exactly CSS-in-JSS. The code below demonstrates the concept using styled-components.

import styled from 'styled-components'; const Text = styled.div` color: white, background: black ` <Text>This is CSS-in-JS</Text>

What the browser sees is something like this:

<style> .hash234dd2 { background-color: black; color: white; } </style> <p class="hash234dd3">This is CSS-in-JS</p>

A new <style> tag is added to the top of the DOM and unlike inline styles, actual CSS styles are generated here. So, anything that works in CSS works in styled components too. Furthermore, this technique enhances CSS, improves readability and fits into the component architecture. With the styled-components lib, you also get SASS support that’s been bundled into the lib.

Nested ternary operator

Ternary operators are popular in React. It’s my go-to operator for creating conditional statements and it works great inside the render() method. For instance, they help you to render elements inline an in the example below, I’ve used it to display the login status.

render() { const isLoggedIn = this.state.isLoggedIn; return ( The user is {isLoggedIn ? 'currently' : 'not'} logged in. ); }

However, when you nest the ternary operators over and over, they can become ugly and hard to read.

int median(int a, int b, int c) { return (a<b) ? (b<c) ? b : (a<c) ? c : a : (a<c) ? a : (b<c) ? c : b; }

As you can see, the shorthand notations are more compact, but they make the code appear messy. Now image if you had a dozen or more nested ternaries in your structure. And it happens a lot often than you think. Once you start with the conditional operators, it’s easy to keep on nesting it and finally, you reach a point where you decide that you need a better technique to handle conditional rendering.

But the good thing is that you have many alternatives that you can choose from. You can use a babel plugin like JSX Control Statements that extends JSX to include components for conditional statements and loops.

// before transformation <If condition={ test }> <span>Truth</span> </If> // after transformation { test ? <span>Truth</span> : null }

There’s another popular technique called iify ( IIFE — Immediately-invoked function expressions). It’s an anonymous function that is invoked immediately after they are defined.

(function() { // Do something​ } )()

We’ve wrapped the function inside a pair of parentheses to make the anonymous function a function expression. This pattern is popular in JavaScript for many reasons. But in React, we can place all the if/else statements inside the function and return whatever that we want to render.

Here is an example that demonstrates how we’re going to use IFFE in React.

{ (() => { if (this.props.status === 'PENDING') { return (<div className="loading" />); } else { return (<div className="container" />); })() }

IIFE’s can have an impact on performance, but it won’t be anything significant in most cases. There are more methods to run conditional statements in React and we’ve covered that in 8 methods for conditional rendering in React.

Closures in React

Closures are inner functions that have access to the outer function’s variables and parameters. Closures are everywhere in JavaScript and you’ve been probably using it even if you’ve not realized yet.

class SayHi extends Component { render () { return () { <Button onClick={(e) => console.log('Say Hi', e)}> Click Me </Button> } } }

But when you are using closures inside the render() method, it’s actually bad. Every time the SayHi component is rendered, a new anonymous function is created and passed to Button component. Although the props haven’t changed, <Button /> will be forced to re-render. As previously mentioned, wasted renders can have a direct impact on performance.

Instead, replace the closures with a class method. Class methods are more readable and easy to debug.

class SayHi extends Component { showHiMessage = this.showMessage('Hi') render () { return () { <Button onClick={this.showHiMessage}> Click Me </Button> } } }

Bonus practice to stop doing: guessing why issues happen in React Debugging React applications can be difficult, especially when there is complex state. If you’re interested in monitoring and tracking Redux state for all of your users in production, try LogRocket. https://logrocket.com/signup/ LogRocket is like a DVR for web apps, recording literally everything that happens on your site. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores. Modernize how you debug your React apps – Start monitoring for free.

Conclusion

When a platform grows, new patterns emerge each day. Some patterns help you improve your overall workflow whereas a few others have significant side effects. When the side effects impact your application’s performance or compromise readability, it’s probably a better idea to look for alternatives. In this post, I’ve covered some of the practices in React that you can avoid because of their shortcomings.

What are your thoughts about React and the best practices in React? Share them in the comments.