React showing 0 instead of nothing with short-circuit (&&) conditional component
Asked Answered
A

12

168

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?

Authorized answered 29/10, 2018 at 14:50 Comment(0)
A
294

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 />}

Aerostatic answered 29/10, 2018 at 14:56 Comment(3)
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!Authorized
You can also use the Boolean operator as well if !!condition confuses you. Something like, { Boolean(condition) && <Component /> }Isodynamic
a react truth table: jsfiddle.net/milahu/k2z3wocm/5Syndesis
B
57

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
Bidget answered 15/8, 2019 at 17:40 Comment(1)
By far the best explanation.Sunglasses
V
21

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.

Vagrom answered 29/10, 2018 at 14:52 Comment(2)
You can also do {profileTypesLoading > 0 && <GeneralLoader />} as the React docs saysStipel
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 && <YourComponent />} // This WILL NOT render component. const xAxis = 0; {xAxis && <YourComponent />}Trabue
M
16

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

{!!profileTypesLoading && <GeneralLoader/>}
Muskogean answered 11/9, 2021 at 0:10 Comment(0)
M
11

A more straightforward approach:

{Boolean(profileTypesLoading) && <GeneralLoader />}
Mclemore answered 8/9, 2021 at 16:3 Comment(0)
T
3

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

Tidings answered 9/5, 2023 at 11:44 Comment(0)
T
2

Change your code like this

{!!profileTypesLoading && <GeneralLoader />}
Tartarous answered 31/3, 2023 at 6:10 Comment(0)
E
1

You can draw an empty react fragment.

{profileTypesLoading ? <GeneralLoader /> : <></>}
Extemporaneous answered 1/3, 2023 at 4:27 Comment(3)
Just return null.Sunglasses
That is not null that is fragment. Means no elementExtemporaneous
The purpose of using <Fragment /> is wrapping children, so if you want to render nothing just return null or falseSunglasses
C
1

From the updated React documentation:

Don’t put numbers on the left side of &&.

For example, a common mistake is to write code like messageCount && <p>New messages</p>. It’s easy to assume that it renders nothing when messageCount is 0, but it really renders the 0 itself!

To fix it, make the left side a boolean: messageCount > 0 && <p>New messages</p>.

https://react.dev/learn/conditional-rendering#logical-and-operator-

Classified answered 6/3 at 8:24 Comment(0)
R
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

Revival answered 3/8, 2021 at 23:25 Comment(0)
S
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.

Silencer answered 21/1, 2023 at 18:37 Comment(1)
It is more verbose than neccessary: You already have created a boolean value which will not be rendered when used like { Boolean(profileTypesLoading) && <GeneralLoader /> }Holyhead
M
0

When condition is 0 or NaN, this JSX expression will be rendered with a 0 or NaN child, respectively:

<ParentComponent>
  {condition && <ChildComponent />}
</ParentComponent>

To get an empty child, you can use this JSX expression instead:

<ParentComponent>
  {!!condition && <ChildComponent />}
</ParentComponent>

!!condition (or equivalently Boolean(condition)) performs boolean conversion.

Explanation

The definition of the && operator:

More generally, the operator returns the value of the first falsy operand encountered when evaluating from left to right, or the value of the last operand if they are all truthy.

So when condition is 0 or NaN, {condition && <ChildComponent />} will be reduced to {condition}, since 0 or NaN is falsy, respectively.

The values that can be used as the children property of a ReactPortal in React 18.2.0:

So when condition is 0 or NaN, {condition} can be used in ParentComponent and will be rendered. Thus to avoid rendering those values, you can use boolean conversion !!condition since boolean are not rendered.

Remark. — Do not confuse the values that can be used as the children property of a ReactPortal with these ones:

The values that can be used as the return value of Component.render are ReactNode.

The values that can be used as the return value of FunctionComponent are ReactElement | null.

Mauve answered 15/4 at 10:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.