Conditional Rendering with Recompose
Let’s take on non-ideal states the functional way.
Non-Ideal State
If you’re a frequent user of web or mobile apps, you’ve probably seen an empty
or error
state on a page. Many times, this will come in the form of a list-view with no results. Handling these states with a cute animation has become commonplace and makes for a delightful user experience.
In one of our React Native apps at Hexient Labs, we handle the offline
condition with a modal overlay:
The modal above tells the user in a friendly way that something is wrong, and how to resolve it. So although the user has to take action, they aren’t too turned off.
These are the exact types of non-ideal states that UI designers and proficient developers have started implementing in their apps to provide a delightful user experience even under bad circumstances.
Recompose?
Recompose by Andrew Clark is an excellent “utility belt” of higher-order functions that allow developers to enhance the functionality of their components via composition.
One of the most simple examples of this is using the setPropTypes
and defaultProps
HOCs to enhance a functional component with some prop-type
requirements and default props.
import React from 'react'
import PropTypes from 'prop-types'
import { compose, defaultProps, setPropTypes } from 'recompose'// Component
const Button = props => <button {...props}>{props.title}</button>// Enhancers
const withPropTypes = setPropTypes({
onClick: PropTypes.func.isRequired,
title: PropTypes.string.isRequired,
})const withDefaultProps = defaultProps({
onClick: () => {},
title: 'Submit',
})export default compose(withDefaultProps, withPropTypes)(Button)
In the example above, the withPropTypes
and withDefaultProps
Higher-Order Components are composed together with the Button component to provide enhanced functionality without coupling them directly to the Button. In fact, these HOCs could be used in any component in our application!
So how can we use this same principle for conditional rendering??
Enter the “branch” HOC
The branch
HOC is another utility function from Recompose that is commonly used with the renderComponent
and renderNothing
HOCs.
Here’s an example of rendering a List and a non-ideal
state when it is empty:
// List.js
import React from 'react'
import PropTypes from 'prop-types'
import { branch, compose, defaultProps, renderComponent, setPropTypes } from 'recompose'
import EmptyState from '../components/common/EmptyState'
import Loader from '../components/common/Loader'// Component
const List = props => (
<ul>
{props.data.map(item => <li key={item}>{item}</li>}
</ul>
)// Enhancers
const withPropTypes = setPropTypes({
data: PropTypes.array.isRequired,
loading: PropTypes.bool.isRequired,
})const withDefaultProps = defaultProps({
data: [],
loading: false,
})const withLoadingState = branch(
props => props.loading,
renderComponent(Loader),
)const withEmptyState = branch(
props => props.data.length === 0,
renderComponent(EmptyState),
)export default compose(
defaultProps,
withLoadingState,
withEmptyState,
withPropTypes
)(List)
The example above composes our propTypes, defaultProps, loading state, and empty state together onto a single functional component List
. The withLoadingState
and withEmptyState
can even be composed onto other components as well!
This is just one example of the power of Recompose- I’d highly recommend checking it out to encapsulate your rendering logic into functional pieces that are easily testable and composable.