188

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?

12 Answers 12

316

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

Sign up to request clarification or add additional context in comments.

3 Comments

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!
You can also use the Boolean operator as well if !!condition confuses you. Something like, { Boolean(condition) && <Component /> }
a react truth table: jsfiddle.net/milahu/k2z3wocm/5
66

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

1 Comment

By far the best explanation.
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.

2 Comments

You can also do {profileTypesLoading > 0 && <GeneralLoader />} as the React docs says
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 />}
17

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

{!!profileTypesLoading && <GeneralLoader/>}

Comments

11

A more straightforward approach:

{Boolean(profileTypesLoading) && <GeneralLoader />}

Comments

4

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-

Comments

3

Change your code like this

{!!profileTypesLoading && <GeneralLoader />}

Comments

3

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

Comments

2

Let’s examine the following JSX expression:

<div>{condition && <Foo />}</div>

When condition evaluates to the values 0 or NaN, the children property of the div element will be rendered as 0 or NaN, respectively.

To prevent rendering for those values, you can modify the expression like this:

<div>{!!condition && <Foo />}</div>

Explanation

The && operator is defined as follows:

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 evaluates to the values 0 or NaN, condition && <Foo /> will evaluate to the values 0 or NaN, respectively.

In React 18, the values that can be assigned to the children property of a ReactPortal are ReactNode values.

The ReactNode values that can be rendered are

The ReactNode values that cannot be rendered are (cf. the implementation)

  • the empty string;
  • boolean;
  • undefined;
  • null.

The values 0 and NaN are number so they will be assigned to the children property of the div element and rendered. To prevent rendering for those values, you can convert them to a boolean with !!condition (or equivalently Boolean(condition)).

Remark. — The values that can be returned by the Component.render function are ReactNode.

The values that can be returned by the FunctionComponent function are ReactElement | null.

Comments

1

You can draw an empty react fragment.

{profileTypesLoading ? <GeneralLoader /> : <></>}

3 Comments

Just return null.
That is not null that is fragment. Means no element
The purpose of using <Fragment /> is wrapping children, so if you want to render nothing just return null or false
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

Comments

-1

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.

1 Comment

It is more verbose than neccessary: You already have created a boolean value which will not be rendered when used like { Boolean(profileTypesLoading) && <GeneralLoader /> }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.