6 Techniques for Conditional Rendering in React, with Examples
Conditional rendering is a fundamental concept in React that allows us to display different UI elements based on specific conditions. It’s an essential tool for building interactive and responsive applications that adapt to user actions and data changes. In this article, we’ll explain the various techniques used in conditional rendering, how they work, and best practices we can follow to create effective and interactive user interfaces.
This article will assume you’re familiar with HTML, CSS, and JavaScript, and that you know at least the basics of React and JSX. Ideally, you’ll also be familiar with debugging tools such as React Developer Tools, which are invaluable for troubleshooting issues related to conditional rendering and visualizing the component state and props.
How to Implement Conditional Rendering in a React Application
Conditional rendering is a powerful tool used to dynamically show or hide UI elements based on certain conditions. This makes our applications more interactive and responsive, because it adapts to user actions and data changes. There are various methods we could use to render elements conditionally in react. They include:
- using an if/else statement
- using a ternary operator
- using logical AND operator
- using switch statements
- using higher-order components
- using element variables
To illustrate how these techniques work, we’re going to build a navigation bar (navbar). A navbar usually has links to various sections of a web app. However, we want the link to our “Cart” page to be hidden from unauthenticated users. To do this, we’ll create React components, define states, and applying conditional logic based on user login status.
Using an If-else Statement
If-else statements are control flow structures that allow us to execute different codes based on whether a condition tests true or false. They can be used to render components based on the result. Let’s look at how this works:
if( condition ){
The task to be done if the condition tests true
}
else {
Tasks to be done when the condition is tested false
}
Now, based on the scenario we gave earlier, we want the navbar to have an extra button if the user is logged in, but to remain in the normal state when the user is logged out. To do this, we’re going to have a JSON object that stores the details of our users, including their login status:
{
"Users": [
{
"Name": "Yemi",
"Age": 23,
"cartProducts": ["Tote bag", "Sports Cap", "Trainers", "Joggers"],
"Status": "loggedIn"
},
{
"Name": "John",
"Age": 30,
"cartProducts": ["Laptop", "Mouse", "Keyboard"],
"Status": "loggedIn"
},
{
"Name": "Alice",
"Age": 25,
"cartProducts": ["Dress", "Shoes", "Bag"],
"Status": "loggedOut"
}
]
}
Next, we’ll create a logic that checks the status of the user and renders the navbar based on the result of the condition:
const user = users[0]; // Assuming we want to check the status of the first user
if (user.status === "loggedIn") {
return <LoggedInNavbar />;
} else {
return <LoggedOutNavbar />;
}
In this code snippet, we access the user.status
property which has a loggedIn
variable. This variable is a Boolean value that indicates whether the user is logged in. Before checking the condition, we create a constant variable named user
and assign it the value of the first element (index 0
) from the users
array. Since users
is an array of user objects, this effectively extracts the login status of the first user object.
Now, let’s look at a breakdown of how we made use of the if-else statement to render elements:
- The if statement takes a condition as its argument. In this case, the condition is
isLoggedIn
. - If the condition is true, the code inside the if statement is executed, which returns a View Cart button element in the navbar.
- If the condition is false, the code inside the else statement is executed, and this renders the navbar without the extra button.
This is one of the most common methods used to conditionally render elements based on conditions in React. However, it can make our code more verbose, especially when dealing with simple conditions. This is where the ternary operator comes in, as it’s a more concise alternative.
Using a Ternary Operator
A ternary operator is also known as a conditional operator. It’s a simpler way of writing an if-else statement. It has three parts:
condition ? trueExpression : falseExpression
- The
condition
is the part to be evaluated. - The
trueExpression
is to be executed if the condition is true. - The
falseExpression
is to be executed if the condition is false.
For instance, the previous code snippet we used to render different navbars can be written as follows using a ternary operator:
return ( <div> {user.status === "loggedIn" ? <LoggedInNavbar /> : <LoggedOutNavbar />}
</div>
);
};
export default App;
Just like in the previous scenario, if the condition is true, the expression LoggedInNavbar
is executed, rendering the LoggedInNavbar
component. Otherwise, the expression LoggedOutNavbar
is executed, rendering the LoggedOutNavbar
component.
When to use the ternary operator
The ternary operator is most suitable for handling simple conditional statements where we have two possible outcomes. However, it may be more appropriate to use an if-else statement for more complex conditional logic involving multiple conditions or nested statements.
Using the Logical AND Operator
An AND
operator is a logical operator used to evaluate more than one condition or expression. It accepts conditions and only tests as true when the two (or more) conditions are tested true.
For example, let’s assume that we only want users who are registered as sellers and are logged in to access the navbar with a dashboard button:
const users = [
{
name: "Yemi",
age: 23,
cartProducts: ["Tote bag", "Sports Cap", "Trainers", "Joggers"],
status: "loggedIn",
userClass: "Admin",
},
];
const user = users[0]; // Assuming we want to check the status of the first user
if (user.status === "loggedIn" && user.userClass === "Admin") {
return <AdminNavbar />;
} else {
return <LoggedOutNavbar />;
}
In this code snippet, we are determining which navbar component to render based on the login status and user class of the first user in the users
array. It utilizes an if-else statement to check if both the user.status
and the user.userClass
properties meet the specified criteria. If both conditions are true, the code inside the if block is executed, returning the AdminNavbar
component.
This indicates that the logged-in user as an admin and should see the admin-specific navbar. If either or both conditions are false, the code inside the else block is executed, returning the LoggedOutNavbar
component. This indicates that the user is either not logged in or not an admin and should see the standard navbar.
Using Switch Statements
Let’s consider a scenario where we have to handle multiple conditional expressions simultaneously. For instance, we’re building an app that has different tiers of users and we need to render different pages for each tiers. If we render each of the pages using an if statement, it could get complicated and even voluminous. This is why the switch statement is a better alternative. Switch statements are constructs used to handle multiple conditional cases in a more organized way. They provide a cleaner syntax when we have a variable to check against multiple possible values.
In conditional rendering, switch statements can be useful when we have a specific variable (or prop) that we want to use to determine which component or content to render based on different cases. Here’s an example of how they work:
switch (user.userClass) {
case "Admin":
return <AdminNavbar />;
case "Customer":
return <CustomerNavbar />; // Assuming a customer should see the CustomerNavBar_
case "Guest":
return <GuestNavbar />; // Assuming a guest should see the GuestNavbar_
default:
return <LoggedOutNavbar />;
}
In this example, the MyComponent
takes a userClass
prop and uses a switch statement to determine which component to render based on the value of userClass
. Each case corresponds to a different userClass
, and the associated component is assigned to the componentToRender
variable.
Switch statements can make our code more readable and maintainable when dealing with multiple conditional cases. They are especially useful when we have a variable that can take on distinct values, and we want to handle each case differently.
What are Higher-order Components?
Higher-order components (HOCs) are a pattern in React that allow us to reuse component logic. They work by acting as functions that take a component and return a new component. The new component is usually a wrapper component that adds additional functionality to the original component. They are used to carry out tasks like adding data fetching, authentication, or conditional rendering.
Higher-order components and conditional rendering
One of the most common ways to make use of HOCs is conditional rendering. This is because they can take a component and return a different component based on a condition. For example, we could use an HOC to conditionally render a component based on whether a user is logged in or not.
Here is an example of how to use a HOC to conditionally render a component:
import React from 'react';
const withLoginCheck = (WrappedComponent) => {
return (props) => {
if (isLoggedIn) {
return <WrappedComponent {...props} />;
} else {
return <p>Please log in to access this content.</p>;
}
};
};
const MyComponent = (props) => {
return <div>Hello, {props.name}!</div>;
};
const App = () => {
return (
<div>
<withLoginCheck(MyComponent) name="John Doe" />
</div>
);
};
In this example, the withLoginCheck
HOC is used to conditionally render the MyComponent
component. If the isLoggedIn
variable is true, the MyComponent
component is rendered. Otherwise, a message is displayed telling the user to log in.
Benefits of using HOCs for conditional rendering
There are several benefits to using HOCs for conditional rendering:
- Reusability. HOCs can be reused in different parts of an application, which can save time and code.
- Maintainability. HOCs can make code more maintainable by encapsulating conditional rendering logic in a single place.
- Testability. HOCs can be easily tested, which can help to improve the code quality.
Using Element Variables
Element variables are another effective way to conditionally render JSX elements in React. They allow us to store JSX elements in variables and then conditionally render those variables based on certain conditions. This approach can make our code more concise and easier to read, especially when dealing with complex conditional rendering scenarios. For instance, let’s consider a scenario where we want to show different pages based on the user’s age:
const user = { age: 25 };
const pageContent = user.age >= 18 ? (
<div>
<h1>Welcome, Adult User!</h1>
<p>You have access to all content.</p>
</div>
) : (
<div>
<h1>Welcome, Young User!</h1>
<p>You have access to age-appropriate content.</p>
</div>
);
return <div>{pageContent}</div>;
In this example, the pageContent
variable holds the JSX element that will be rendered based on the user’s age. The conditional operator is used to determine whether to render the content for an adult user or a young user. This approach effectively separates the conditional logic from the JSX rendering, making the code more readable and maintainable.
Handling Loading State when Rendering Components
Loading state is a concept in React that informs users that an application or website is actively processing or retrieving data. Conditional rendering is one of the methods used to handle loading state, because it often involves dynamically displaying different UI elements based on the state of the data loading process. This improves user experience, as it ensures that there’s feedback at every stage of the application.
For instance, we might conditionally render a loading indicator while data is fetching and then switch to rendering the actual data once it’s available. This ensures that users see relevant information at the appropriate time.
Here’s an example of how to use conditional rendering to display a loading indicator while data is fetching:
import React, { useState, useEffect } from 'react';
const DataFetcher = () => {
const [isLoading, setIsLoading] = useState(true);
const [data, setData] = useState([]);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/posts')
.then((response) => response.json())
.then((json) => {
setData(json);
setIsLoading(false);
});
}, []);
return (
<div>
{isLoading ? <p>Loading...</p> : data.map((post) => <p key={post.id}>{post.title}</p>)}
</div>
);
};
In this example, the isLoading
state variable is used to track whether the data has been loaded. The useEffect
hook is used to fetch data from an API, and once the data is available, the isLoading
state is updated to false. The conditional rendering logic ensures that the loading indicator is rendered while the data is loading, and once the data is available, the list of posts is rendered instead.
Using Component State for Dynamic Rendering
Component state is mutable data stored in a component that allows us to store and manage information specific to that component. It’s managed within the component itself and can be updated using the setState()
method. When the state changes, React re-renders the component and its children, allowing us to dynamically update the UI based on the new state value. For instance:
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
In this example, the Counter
component maintains a state variable count and updates it using the setCount()
method. The onClick
handler triggers the update, and React re-renders the component, updating the displayed count value.
Using Props for Dynamic Rendering
Props are arguments consisting of data passed down from parent components to child components. They provide a way to communicate data and control the behavior of child components from the parent. When the parent component updates its props, the child component receives the new data and re-renders accordingly.
For example:
import React from 'react';
const Greeting = ({ name }) => {
return (
<p>Hello, {name}!</p>
);
};
const App = () => {
return (
<div>
<Greeting name="John Doe" />
<Greeting name="Jane Doe" />
</div>
);
};
In this example, the Greeting
component receives a name
prop from the App
component. The Greeting
component re-renders whenever the App
component updates the name
prop, displaying the appropriate greeting for each individual.
Now, just like the other method we mentioned, the changes in component state or props can trigger conditional rendering, dynamically showing or hiding specific elements based on the updated data. These techniques allow us to build UI components that adapt and change based on user interactions, data changes, and the flow of information within the application. For example:
import React, { useState } from 'react';
const UserStatus = ({ isAdmin }) => {
let statusElement;
if (isAdmin) {
statusElement = <p>Administrator</p>;
} else {
statusElement = <p>Customer</p>;
}
return (
<div>
{statusElement}
</div>
);
};
In this example, the UserStatus
component conditionally renders a different UI element based on the value of the isAdmin
prop. When isAdmin
tests true, the administrator message is rendered. Otherwise, the standard user message is displayed.
Handling Errors in Conditional Rendering
Handling errors in conditional rendering is crucial for creating robust and user-friendly React applications. It involves gracefully handling unexpected situations and providing appropriate feedback to users, preventing them from encountering confusing or broken UI elements. These errors may come up due to:
- malfunctions while fetching data
- invalid user input
- component configuration errors
How to handle errors in conditional rendering
-
Make use of error boundary components to capture and handle errors within their child components.
-
Implement fallback UI elements to display when errors occur. These elements can provide informative messages, alternative content, or suggest retry options.
-
Implement error logging mechanisms to record and track errors for debugging and analysis. This helps identify recurring issues and improve error-handling strategies.
-
Provide clear and actionable error notifications to users, informing them of the issue and suggesting potential solutions or workarounds.
An example of handling errors in conditional rendering
import React from 'react';
const DataFetcher = () => {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then((response) => response.json())
.then((json) => setData(json))
.catch((err) => setError(err));
}, []);
if (error) {
return <p>Error fetching data: {error.message}</p>;
}
if (!data) {
return <p>Loading...</p>;
}
return <div>{data.title}</div>;
};
In this example, the DataFetcher
component handles potential errors by checking the error state variable. If an error occurs, an error message is displayed. If the data is still loading, a loading indicator is shown. Once the data is fetched successfully, the actual data is rendered.
By adding effective error-handling techniques in conditional rendering, we can create React applications that are resilient, user-friendly, and capable of gracefully handling unexpected situations.
Best Practices for Conditional Rendering
As we mentioned earlier, conditional rendering is a fundamental concept in React that allows us to dynamically display different UI elements based on specific conditions. To ensure the effective and efficient use of conditional rendering, there are best practices to follow, including:
-
Keep conditional rendering logic clear and easy to understand. Avoid complex nested conditions or overly intricate logical expressions.
-
Leverage component state and props to control conditional rendering. Stateful components can manage internal state changes, while props can be used for data-driven rendering based on external sources.
-
Consider extracting complex conditional logic into separate functions or components. This enhances code reusability and maintainability.
-
Implement error handling mechanisms to gracefully handle unexpected situations and provide informative error messages to users.
-
Strike a balance between performance optimization and code readability. Use conditional rendering effectively, but don’t sacrifice clarity for the sake of optimization.
-
Thoroughly test conditional rendering logic to ensure it behaves as expected under various conditions and edge cases.
Conclusion
In this article, we looked at several techniques for conditional rendering in React, including if-else statements, ternary operators, switch statements, higher-order components, and element variables. We also discussed ways to effectively handle errors, and best practices for using conditional rendering efficiently. By understanding and implementing these techniques, we can build efficient React applications that are flexible, responsive, and user-friendly.