Without if statements in JSX, how do you control your application's flow? Let's explore how to render or NOT render elements in React.
You can't embed if
statements in JSX. So how do you control what is displayed? Controlling flow through your application is fundamental to programming, and it's no different in React. In this article, we are going to answer the question: How do I show or hide something in React?
I started to brainstorm the different ways to show and hide things in React, and it turns out there are more ways than you might think! We'll cover some of the pros and cons to each approach, and how you can test for the presence of an element with React Testing Library.
Full source code can be found here.
Returning Null
In modern React, a component is little more than a function whose job it is to return the value that is to be rendered. Just like regular functions, functional components can have multiple return values. If what the component renders is an "all or nothing" situation, the simplest way to control whether an element is rendered is to avoid returning any JSX at all, and return null
instead.
Because this if statement is not embedded inside of JSX but is just part of the regular JavaScript portion of the function, you are free to use any sort of JS construct that you like. In this example, if the product is not available, we're just going to return null
.
const AddToCart = ({ available }) => {
if (!available) return null;
return (
<div className="full tr">
<button className="product--cart-button">Add to Cart</button>
</div>
);
};
Ternary Display
When you need to control whether one element vs. another is displayed, or even one element vs. nothing at all (null
), you can use the ternary operator embedded inside of a larger portion of JSX.
In this case, if there are no products remaining, we will display "Sold Out"; otherwise we will display the number of products remaining.
<div className="half">
<p>{description}</p>
{remaining === 0 ? (
<span className="product-sold-out">Sold Out</span>
) : (
<span className="product-remaining">{remaining} remaining</span>
)}
</div>
Shortcut Display
If you want to only display something if a value is true
and there is nothing to display if the result is false
, there is a shortcut rather than having null
on the falsey side of a ternary operator. It involves using a conditional inside of your JSX that looks like checkIfTrue && <span>display if true</span>
. Because if statements that use &&
operands stop as soon as they find the first value that evaluates to false, it won't reach the right side (the JSX) if the left side of the equation evaluates to false.
Let's see this in action! We will only display the rest of the product name if it has something to display:
<h2>
<span className="product--title__large">{nameFirst}</span>
{nameRest.length > 0 && (
<span className="product--title__small">{nameRest.join(" ")}</span>
)}
</h2>
I have to point out that this causes problems in React Native, where it doesn't know to handle false
during output, and ends up causing an error. In React Native you should use a ternary operator with null
being returned on the false side:
<h2>
<span className="product--title__large">{nameFirst}</span>
{nameRest.length > 0 ? (
<span className="product--title__small">{nameRest.join(" ")}</span>
) : null}
</h2>
Using Style Property
Up until this point we have chosen between rendering an element or not. What if we wanted to render an element but not have it seen? At this point we have a few options to focus on — the first being directly modifying the HTML element's style
property, setting CSS attributes such as display
and opacity
. In this short example below we will set the display
property to be either block
or none
depending on the value contained within showInfo
. Once again, a ternary operator is used inside embedded JSX to control the flow of our application.
<div style={{ display: showInfo ? "block" : "none" }}>info</div>
Modifying CSS Classes
Along the same theme as modifying style
attributes, we can modify which class an element has, giving us the ability to control an element's display
, opacity
, or even hiding it off the side of the screen as might be done with a hamburger menu when it is in its closed state.
In the example below, the nav
element is off the left side of the screen with left: -200px
, but when the class open
is added to the nav
element, it transitions to having left: 0px
, and suddenly it is visible again.
nav {
position: fixed;
left: -200px;
width: 200px;
padding: 1rem;
transition: 0.3s all ease;
z-index: 1000;
height: 100vh;
background: #cfd8dc;
}
nav.open {
left: 0px;
}
We can toggle this CSS class using state that is toggled within the onClick
of a button (the hamburger), choosing to add a class or not with a ternary condition className={open ? "open" : null}
.
const Nav = () => {
const [open, setOpen] = React.useState(false);
return (
<nav className={open ? "open" : null}>
<button
onClick={() => {
setOpen(!open);
}}
>
hamburger
</button>
<ul>{/* elements */}</ul>
</nav>
);
};
Visibility Animation with react-spring
Rather than manipulating classes and style attributes ourselves, we can reach for a third-party library to do it for us. In this case we are using react-spring, which can toggle any numerical CSS attribute using physics-based properties such as the mass
, tension
, and friction
. If those aren't completely obvious to you (as is definitely my case!), there is a neat react-spring visualizer available to help you get the settings right.
import { useSpring, animated } from "react-spring";
const SpringIn = ({ children }) => {
const props = useSpring({
opacity: 1,
from: { opacity: 0 },
config: { mass: 10, tension: 10, friction: 10 }
});
return <animated.div style={props}>{children}</animated.div>;
};
With our custom component SpringIn
, simply wrapping <SpringIn><div>any content</div></SpringIn>
, we are able to have that element fade in using react-spring
.
Testing Existence with React Testing Library
Testing should be an important part of your React development process, and using React Testing Library we can test for the presence or lack of presence of an element being rendered.
The first example uses getByText
to find the element, and we expect it toBeInTheDocument
, whereas the second example uses queryByText
to verify it toBeNull
. We switched from getByText
to queryByText
because getByText
will raise an error if it can't find the element that you are looking for, but in the second example, that's exactly what we expect to find!
import React from "react";
import { render, fireEvent } from "@testing-library/react";
import { AddToCart, Nav } from "./App";
test("renders cart button when available", () => {
const { getByText } = render(<AddToCart available={true} />);
expect(getByText(/cart/i)).toBeInTheDocument();
});
test("hides cart button when not available", () => {
const { queryByText } = render(<AddToCart available={false} />);
expect(queryByText(/cart/i)).toBeNull();
});
Testing for Classes with React Testing Library
We can also use React Testing Library to check if an element has a certain CSS class or not. In the example below, our nav
is originally hidden, which means that it doesn't have the open
class, but after toggling state by clicking on the hamburger menu, we can verify that it correctly has the open
class.
test("adds class to nav when toggled", () => {
const { getByTestId } = render(<Nav />);
const navElement = getByTestId("nav");
expect(navElement).not.toHaveClass("open");
fireEvent.click(getByTestId("hamburger"));
expect(navElement).toHaveClass("open");
});
Conclusion
In this article we covered six different ways to show or not show an element in React. Sometimes we chose to not have it rendered at all, using a few different types of conditional statements, but we also looked at how to have the element rendered, but to not have it visible to the user, playing with style attributes and CSS classes. Lastly we used React Testing Library to verify that a certain element was correctly rendered, or not rendered.