158

I have the following simple short-circuit statement that should show either a component or nothing:

{profileTypesLoading && <GeneralLoader />}

If the statement is false, it renders a 0 instead of nothing.

I have done a console.log(profileTypesLoading) just to see quickly what the status of the profileTypesLoading property is and it's either 1 or 0 as expected. 0 should be false... causing nothing to render. Right?

Any idea why this would happen?

Gurnzbot
  • 3,742
  • 7
  • 36
  • 55

10 Answers10

272

Since your condition is falsy and so doesn't return the second argument (<GeneralLoader />), it will return profileTypesLoading, which is a number, so react will render it because React skips rendering for anything that is typeof boolean or undefined and will render anything that is typeof string or number:

To make it safe, you can either use a ternary expression {condition ? <Component /> : null} or boolean cast your condition like {!!condition && <Component />}

enapupe
  • 15,691
  • 3
  • 29
  • 45
  • 2
    I'll accept this once I'm allowed in 3 min... but thank you, that makes sense. I'm surprised this is not in any of the explanations I've read on this method. It's always just been written like I had written. No casting. Lesson learned! – Gurnzbot Oct 29 '18 at 15:03
  • 24
    You can also use the `Boolean` operator as well if `!!condition` confuses you. Something like, `{ Boolean(condition) && }` – vijayscode May 10 '19 at 06:51
  • a react truth table: https://jsfiddle.net/milahu/k2z3wocm/5/ – milahu Aug 18 '20 at 08:53
46

0 is a falsy value, so when it is evaluated by &&, it returns 0. However, 0 is renderable by React because it is a number:

// Renderable values
1 && <GeneralLoader /> // => Renders <GeneralLoader />
"a string" && <GeneralLoader /> // => Renders <GeneralLoader />
0 && <GeneralLoader /> // => Renders '0'

// Non-renderable values
false && <GeneralLoader /> // => Renders nothing
null && <GeneralLoader /> // => Renders nothing
undefined && <GeneralLoader /> // => Renders nothing

TLDR

This is because of how javascript itself process [truthy and falsy values][1]:

In JavaScript, a truthy value is a value that is considered true when encountered in a Boolean context. All values are truthy unless they are defined as falsy (i.e., except for false, 0, "", null, undefined, and NaN).

When used with the && operator, the returned value depends on the left value:

  • If the left value is truthy, the right value is returned.
  • If the left value is falsy, its value is returned.

Examples:

// Truthy values
1 && "hello" // => "hello"
"a string" && "hello" // => "hello"

// Falsy values
0 && "hello" // => 0
false && "hello" // => false
null && "hello" // => null
undefined && "hello" // => undefined

The same rules applies to JSX because it is [a syntax extension to JavaScript][2]. However, the issue is that **

The issue is that 0 is a falsy value, so when it is evaluated by &&, it returns 0. However, 0 is renderable by React because it is a number

// Renderable values
1 && <GeneralLoader /> // => Renders <GeneralLoader />
"a string" && <GeneralLoader /> // => Renders <GeneralLoader />
0 && <GeneralLoader /> // => Renders 0

// Non-renderable values
false && <GeneralLoader /> // => Renders nothing
null && <GeneralLoader /> // => Renders nothing
undefined && <GeneralLoader /> // => Renders nothing
Giovanni Benussi
  • 3,102
  • 2
  • 28
  • 30
18

This would solve the problem:

{!!profileTypesLoading && <GeneralLoader />}

As it will convert 0 to false. The reason is when it's 0 the next condition doesn't get executed and it behaves like a number in JavaScript so double negation helps here.

Sakhi Mansoor
  • 7,832
  • 5
  • 22
  • 37
  • 1
    You can also do `{profileTypesLoading > 0 && }` as the [React docs says](https://reactjs.org/docs/jsx-in-depth.html#booleans-null-and-undefined-are-ignored) – Guilherme Samuel Feb 07 '21 at 03:13
  • But sometimes if your variable is Number, like coordinates, which is zero (0), then it will give false, which is unexpected thing. Examples: `/// This WILL render component. const xAxis = 100; {xAxis && } // This WILL NOT render component. const xAxis = 0; {xAxis && }` – Lauris Kuznecovs Nov 11 '21 at 09:20
13

You can use the double Bang (!!). This returns the boolean true/false association of a value and will not render a 0.

{!!profileTypesLoading && <GeneralLoader/>}
michael d.
  • 171
  • 1
  • 3
11

A more straightforward approach:

{Boolean(profileTypesLoading) && <GeneralLoader />}
Josh M.
  • 387
  • 4
  • 13
2

React will skips rendering type [boolean, undefined, null], and will render [string, number, ...]

Ziqiang He
  • 21
  • 1
1

You can draw an empty react fragment.

{profileTypesLoading ? <GeneralLoader /> : <></>}
nntona
  • 409
  • 4
  • 9
1

Change your code like this

{!!profileTypesLoading && <GeneralLoader />}
pinku kumar
  • 99
  • 1
  • 1
0

To evaluate a false condition at first, using a const with ternary is a easy way. Example:

On this case a simple button is shown if someCollecctionProperty is empty else, a button with some-options-menu will be shown (Material UI Example)

export default function MyComponent({ obj }) {

  const jsxResponse = !obj.someCollecctionProperty.length ? <Button>No Action</Button> 
    : 
    <>
      <Button 
        aria-label="more"
        aria-controls="long-menu"
        aria-haspopup="true" 
        variant="contained" 
        onClick={handleClick} 
        color={'primary'}>
          <ThumbUpAltOutlinedIcon/>OK
      </Button>
      <Menu
        id="long-menu"
        anchorEl={anchorEl}
        keepMounted
        open={open}
        onClose={handleClose}
      >
        {obj.someCollecctionProperty.map((option) => (
          <MenuItem key={option.id} onClick={handleClose}>
            <Avatar variant="square" alt={option.name} src={option.url}/>{option.configName}
          </MenuItem>
        ))}
      </Menu>
    </>;

  return (<div>{jsxResponse}</div>);
}

jsxResponse is the rendered component, 0 on view can be avoid with this

nilsoviani
  • 344
  • 5
  • 16
0

This code

{ Boolean(profileTypesLoading) ? <GeneralLoader /> : <></> }

also solves the problem. Since I am a beginner I don't know if this has drawbacks or side effects.

Giorgio Tempesta
  • 1,816
  • 24
  • 32
asd1122
  • 11
  • It is more verbose than neccessary: You already have created a boolean value which will not be rendered when used like `{ Boolean(profileTypesLoading) && }` – cachius May 04 '23 at 16:33