Blog post

Lesser spotted React mistakes: What are we even rendering?

Gabriel Vivas photo

Gabriel Vivas

Product Manager

5 min read

  • SonarCloud
  • SonarLint
  • SonarQube
  • TypeScript
  • JavaScript
  • React
  • ESLint

This series is dedicated to the small, but common pitfalls and errors you can encounter when writing React code. Whether an experienced JavaScript | TypeScript developer or just starting out, the results can be surprising.


These are the kind of issues you want to catch early in your IDE before you spend hours debugging. You can copy/paste the code examples in VS Code with the SonarLint plugin if you want to see them for yourself and try to catch them before they happen to you!

Render what?

In this third and final installment of the series, we’ll look at three subtle defects that go from rendering unexpected characters, to silently not rendering anything at all. 


These insidious mistakes are small in character count, but produce real problems that are hard to track once they find their way into your codebase 😈.

Render non-boolean values

When using JSX in React you can conditionally render your components. One common way to do it is using the logical AND operator && to render when there is a truthy value or show nothing in the other case.


Here’s an example Component that greets people when necessary, let’s call it Greeting.tsx since it uses TypeScript:

import React from "react";

interface GreetingProps {
  people: Array<string>;
}

export function Greeting({ people }: GreetingProps) {
  return <div>{people.length && "Hello people"}</div>;
}


If you are following along at home in VS Code, make sure you have a tsconfig.json too:

{
    "compilerOptions": {
      "jsx": "react"
    }
}

Alternatively, if you’re into terminals and you already have the npm command, you can run:

$ npx tsc –init
$ npm i react

Well done! You’ve got yourself some over-engineered greeting with optional rendering. Nice.


However, this pattern does not always work as expected. We managed to introduce a bug already. Look at this other example of a Component that simply shows a list of names. Try to spot the issue:

export function PeopleGreetings({ people }: GreetingProps) {
  return (
    <div>
      <ul>
        {people.length &&
          people.map((name) => <li key={name}>Hello {name}!</li>)}
      </ul>
    </div>
  );
}

Surprisingly, when people is an empty Array this will render “0” instead of nothing 🤯.


Since the number zero is a falsy value there would be no list to show. However, React treats the number 0 as a legitimate value to render as a String. More generally, React will render all non-boolean falsy value types, like Number or BigInt. This includes NaN, which might also come from an arithmetic issue.


Furthermore, if you are using React Native, your render method will actually crash with 0 or NaN values 💥.


To be safe, you can use a ternary to explicitly return null if that is what you intended:

export function PeopleGreetings2({ people }: GreetingProps) {
  return (
    <div>
      <ul>
        {people.length
          ? people.map((name) => <li key={name}>Hello {name}!</li>)
          : null}
      </ul>
    </div>
  );
}

You could also write an expression that evaluates to a real boolean, like so:

export function PeopleGreetings3({ people }: GreetingProps) {
  return (
    <div>
      <ul>
        {people.length > 0 &&
          people.map((name) => <li key={name}>Hello {name}!</li>)}
      </ul>
    </div>
  );
}

Now, how to detect this issue before it creates an obscure bug?


Unfortunately, because this is related to how React components work, JavaScript thinks this is what you want. Not even TypeScript could detect an issue.


Of course, if you’re using SonarLint, you’re covered 😎. See below:

Lesser Spotted React mistakes sonarlint

In case you’re wondering, Eslint also has a rule for detecting this. Although you might need to add the eslint-plugin-react plugin and configure it in your .eslintrc.json file. Note that this rule is NOT included by default in the React plugin’s “react/recommended” setting, you need to add it manually even if you are already extending:

$ npm install eslint eslint-plugin-react --save-dev

$ cat .eslintrc.json

{

  "extends": [

    "plugin:react/recommended"

  ],

  "rules": {

    "react/jsx-no-leaked-render": [

      "error",

      { "validStrategies": ["ternary", "coerce"] }

    ]

  }

}

Render your comments

Not all comments are made alike. As you know, there are a few ways to create comments in JavaScript and TypeScript:

// I’m a single line comment

/*

 * I’m a multiline comment

 */

const today = Date.now() // Inline comment here

That’s all fine. The problem comes when you try to do that within JSX in a React Component. Can you guess what this will render?

function Secrets() {

  return <div>

    // nothing here

  </div>

}

More than a div. It turns out it will render the string // nothing here too 😅. This will also happen if you use the multiline comment syntax:

function Secrets() {

  return <div>

    /* nothing here */

  </div>

}

Besides the div, that will render the string /* nothing here */ 🙃. But why?


What we are commonly using in React Components is JSX, not HTML or XML, even if it looks similar. Being a syntax extension, it needs some preprocessing to be converted into valid ECMAScript code.


It can help to see what the plain JavaScript version of the JSX would look like. It’s just a function call:

function Secrets() {

  return React.createElement("div", null, "/* nothing here */");

}

If you know your HTML or XML, you might dare to try this version:

function Lies() {

  return <div>

    <!-- Appearances can be deceiving -->

  </div>

}

Which will actually crash your component with a SyntaxError caused by an unexpected token, the exclamation mark 💥.


How are you supposed to add comments in JSX? Well, you’ll need to use curly braces { } to tell JSX that you are embedding a JavaScript expression and not a regular String:

function LGTM() {

  return <div>

    {/* I’m for your eyes only */}

  </div>

}

As with the first pitfall we shared, TypeScript won’t help us since it has no way to know if we really want the string // nothing here.


SonarLint has your back once again here. Both single-line and multi-line comments will be detected as bugs as you write them in VS Code or your IDE of choice 👍.


Eslint has a “recommended” rule for this one. You will need to add the eslint-plugin-react plugin and explicitly extend it in your .eslintrc.json file:

$ npm install eslint eslint-plugin-react --save-dev

$ cat .eslintrc.json

{

  "extends": [

    "plugin:react/recommended"

  ]

}

Render a lot of nothing

This is a silly one that can make you pull your hair out 🧑‍🦲.


As we code all day, we can become blind to subtle details. The real problem comes when there is no issue raised by our tools, in this case TypeScript or React.


Try to find the problem with this small component:

const HeadScratcher = (props) => {

  <div>

    <h1>I do nothing, really</h1>

  </div>

};

Did you see that one? Or more precisely, did not see the missing return statement?


React will happily render a lot of nothing, without any errors, and leave you alone to figure it out.


This can happen easily if you are using regular parenthesis where no return is needed, and maybe mixed up with curly braces.


This is how that should look if you wanted to render something:

const HeadScratcher = (props) => (

  <div>

    <h1>I do nothing, really</h1>

  </div>

);

Maybe hard to catch if you look too quickly 👀. It could be in the newspaper or in a puzzle book.


Anyway, using parenthesis means now we are implicitly returning a single JSX expression. That works as expected.


You could also be more explicit with the return statement while keeping the curly braces:

const HeadScratcher = (props) => {

  return (

    <div>

      <h1>I do nothing, really</h1>

    </div>

  );

};

As expected, SonarLint will help you while you’re writing the code. It will raise an issue since this is very likely an annoying bug 🐛.


In this case, there is no Eslint rule available. There is react/require-render-return that can help detect missing return statements in React Classes. Unfortunately, it won't work for functional components like the ones in our examples. Nevertheless, it comes with “react/recommended” and it is better than nothing!

Prevent issues before they happen

As you see, there can be some non-obvious edge cases when rendering components in React. 


Some of these cases can be detected with Eslint, although you might need some setup as described in each case. Others are more subtle.


By default, SonarLint will detect all these issues and warn you as they come up, so you can fix them on the spot, without losing focus, allowing you to write clean code. If you want to dig deeper, SonarLint will also provide an explanation as to why they happen in the first place🧐. Sort of what we did in this article.

That’s a wrap

Thank you for following this blog series about React and SonarLint, it's been fun! We are already preparing our next series about JavaScript and SonarLint, designed to help you Clean as You Code. See you in a whale 🐋!


If you liked this post, send us a Tweet @SonarSource or a comment in the Community. We’d love to hear about your experience.


Read more about these rules in our catalog:


[Previous posts]

Free Video! Best practices to improve your JavaScript coding.
Watch Now