So if you've spent a measure of time with React you'll come across the scenario of needing to render an element based on a condition.
For example, what if you had a component that counted something. And you wanted to show the actual count in a component. However, if it ever dropped below 1, you might want show a message in its place. Informing the user something or presenting a new call to action to do something else.
You could write it in a ternary operator like this:
import { useState } from "react";
import RenderIf from "components/RenderIf";
export const Sample = () => {
const [count, setCount] = useState(2);
const increment = () => {
let newCount = count;
setCount((newCount += 1));
};
const decrement = () => {
if (count === 0) return;
let newCount = count;
setCount((newCount -= 1));
};
return (
<>
<button onClick={increment}>Add</button>
<button onClick={decrement}>Subtract</button>
{count > 0
? <p>I have {count}, and that's pretty cool.</p>
: <p>Sorry, I'm all out.</p>
}
</>
);
};
This works OK, but in my opinion doesn't read as easy as another option. What could a component look like to handle this use case?
Proposal
We can build a component that renders the content it wraps based on a condition we feed it. If that condition isn't true then we'll render something else. This will handle this use case with a little more finesse, IMO. 😎
First let's make a component called RenderIf. Below are the JSX and TypeScript version. Pick your poison.
JSX:
import { ReactNode } from "react";
const RenderIf = ({ children, isTrue, fallback }) => {
return isTrue ? children : fallback;
};
export default RenderIf;
TypeScript:
import { ReactNode } from "react";
type Props = {
children: ReactNode;
isTrue: boolean;
fallback?: any;
};
const RenderIf = ({ children, isTrue, fallback }: Props) => {
return isTrue ? children : fallback;
};
export default RenderIf;
Esplaining what's happenin'
This component that we've made has 3 props being passed in:
- children
- isTrue
- fallback
We destructure those props and pass them into the component. Children is whatever element this ** **component is wrapping. Then we pass the condition of when to render the wrapped element with the isTrue prop.
Whenever this condition is true (or truthy) it will render the wrapped element. If the condition is NOT true, then it renders whatever we pass in the fallback argument.
In TypeScript I've set this to be an optional argumentin the type of Props. Why? I may not want to always pass a fallback element. So if I pass no fallback argument prop then will return undefined and a blank component will render.
In the JSX version of , this happens naturally.
So your fallback argument can be a message saying, 'Hey you're out of counts' or it could be a button to buy more counts. You get the idea.
Example use in an app:
import { useState } from "react";
import RenderIf from "components/RenderIf";
export const Sample = () => {
const [count, setCount] = useState(2);
const increment = () => {
let newCount = count;
setCount((newCount += 1));
};
const decrement = () => {
if (count === 0) return;
let newCount = count;
setCount((newCount -= 1));
};
return (
<>
<button onClick={increment}>Add</button>
<button onClick={decrement}>Subtract</button>
<RenderIf isTrue={count > 0} fallback={<p>Sorry, I'm all out.</p>}>
<p>I have {count}, and that's pretty cool.</p>
</RenderIf>
</>
);
};
Go build some stuff
So now you've got a handy component you can use over and over again to render conditional elements in React. Maybe you want to extend this component to do other things too. What else could you see it do? Let me know what you think and if you'd take a different approach.
Good luck and happy building. 🔨
Top comments (0)