React components don’t have to render anything

Eleni Chappen
20spokes Whiteboard
2 min readMar 31, 2017

Friendly reminder: React components don’t have to render any markup. It’s totally okay to do this:

render() { return null; }

Why is this helpful? A few reasons:

First, it allows components to control their own rendering logic. This is especially helpful for components that render error or warning messages, or for loading screens.

This is a common pattern found in container components:

const Container = (props) =>
<div>
{ props.errors && <ErrorMessages errors={props.errors} /> }
{ props.loading && <Loader /> }
<SomeOtherComponents />
....

It’s not that this pattern is bad. It’s just not as maintainable as it could be. As your app grows, it may be necessary to move your error messages or loading screen somewhere else. Instead of having to rewrite this rendering logic in a new container, you could just have ErrorMessages handle its own rendering:

const ErrorMessages = (props) => {
if (props.errors) {
return <div>{ errors.map((err) => <span>{err}</span>); }</div>
}
return null;
}

This frees up your containers to just be … containers:

const Container = (props) =>
<div>
<ErrorMessages errors={props.errors} />
<Loader isLoading={props.loading} />
<SomeOtherComponents />

Components can now be added and removed from containers with ease.

Another way that rendering null comes in handy is to take advantage of React’s lifecycle methods for functionality that has nothing to do with markup, like timers.

One of our projects at 20spokes involves a large form that needs to automatically cancel at different steps after a period of user inactivity. Each step of the form has its own specifics on how long the step remains active, and what to do once that step times out. For this I created a generic Timer component that’s added to any form step that needs it:

const TIMEOUT_SECONDS = 30class EnterPhoneNumberStep extends Component {  cancelTransaction() {
// cancel transaction here
}
render() {
<div>
<Timer
seconds={TIMEOUT_SECONDS}
onTimeout={this.cancelTransaction.bind(this)}
/>
<h1>Please enter your phone number...

Since rendering null still calls lifecycle methods, my Timer component can use them to start and clear the timer:

class Timer extends Component {

componentWillMount() {
this.timeout = setTimeout(() => {
this.props.onTimeout();
}, this.props.seconds * 1000);
}
componentWillUnmount() {
clearTimeout(this.timeout);
}
render() { return null; }
}
Timer.propTypes = {
seconds: PropTypes.number.isRequired,
onTimeout: PropTypes.func.isRequired
}

(Sidenote: When combining any timeouts or intervals with React components, it’s really important that they are cleared when the component un-mounts, or else you’ll get some ghost functionality.)

Since I’m adding this Timer into a form step component, its timer will begin only when the user enters that step of the form (assuming the form steps are mounting at the proper times.)

Taking advantage of the fact that components don’t have to render anything has made my components much more modular and compliant with the Single Responsibility Principle. As with any other code, its best when React components have only one major purpose.

--

--