Loading State Trick for Stateless Functional Components in React
I want to share with you a little trick I’ve been using lately with stateless functional components in React. This is probably really old news to some of you, but I’m hoping there are a few of you who don’t know this one yet.
If you’re getting started with React, and especially if you’re using Redux, you’ve probably come across the concept of container and presentational components.
Container components provide data to nested components. These components will generally use lifecycle methods, such as componentDidMount
, to fetch some data and pass it down.
Presentational components, on the other hand, are used solely for displaying data passed into them and generally do not use any lifecycle methods. Often, if a presentational component does not require any lifecycle method (other than the required render
), then this component is written as a stateless functional component.
A stateless functional component is simply a function that returns the markup (React functions or JSX). Props are passed as arguments to the function, and then utilized within the markup. Let’s make a simple example of a stateless functional component that expects an array of items, and displays each one of those items.
import React, { PropTypes } from 'react'
const DisplayItems = ({ items }) => (
<div className="items">
{items.map((item, index) => (
<div className="item" key={index} {...item} />
))}
</div>
)
Display.propTypes = {
items: PropTypes.array,
}
export default DisplayItems
I hope the ES6 doesn’t scare you. I’ve created a function called DisplayItems
. This function expects a props
object as an argument, and I’m using ES6 destructuring to make it clear that I’m expecting and will use an items
property on the props
object.
Then, I am using the implicit return
feature of ES6 arrow functions to return the markup (in this case JSX, it’s all I really use) from within the body of the function.
Then, I map
over each item, returning another div
. React requires that we add a key attribute to items that are dynamically populated, which is often the case with displaying arrays. In this case, it was easy to use the index, but it might be more useful to use a property on the item
itself in your case.
Lastly, I am using ES6’s Object spread syntax to pass all props from item
into the div
. In your example, it is possible that this inner item div
would be another presentational component that needs access to all the props
on item
.
Thus, if we used this in our app, regardless of how many items
we pass to this stateless functional component, React would give us the wrapping div
, and then any div
s generated by the map function.
But what if you currently don’t have any items and you don’t want to display the wrapping div
until you have items
?
It is possible to reconfigure this component to display alternative markup if there are no items to display.
Imagine that I have built another component called LoadingSpinner
which simply shows the user a loading spinner animation. Perhaps it looks something like this:
import React, { PropTypes } from 'react'
const LoadingSpinner = () => (
<div className="loading_spinner-wrap">
<svg
className="loading_spinner"
width="60"
height="20"
viewBox="0 0 60 20"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="7" cy="15" r="4" />
<circle cx="30" cy="15" r="4" />
<circle cx="53" cy="15" r="4" />
</svg>
</div>
)
export default LoadingSpinner
And maybe I’ve written some basic styles for it to animate the circles:
.loading_spinner-wrap {
width: 100%;
padding: 50px 0;
}
.loading_spinner {
display: block;
margin: 0 auto;
fill: #000;
circle {
animation-name: upAndDown;
animation-duration: 2s;
animation-timing-function: cubic-bezier(0.05, 0.2, 0.35, 1);
animation-iteration-count: infinite;
&:nth-child(2) {
animation-delay: 0.18s;
}
&:nth-child(3) {
animation-delay: 0.36s;
}
}
}
@keyframes upAndDown {
0% {
opacity: 0;
transform: translateY(0);
}
25% {
opacity: 1;
transform: translateY(-10px);
}
75% {
opacity: 1;
transform: translateY(-10px);
}
100% {
opacity: 0;
transform: translateY(0);
}
}
Now, I can set up my DisplayList
component to either render the LoadingSpinner
component, or my list, depending on whether any items
have been passed to it. Like so:
import React, { PropTypes } from 'react'
import LoadingSpinner from './LoadingSpinner'
const DisplayItems = ({ items }) => {
return items.length ? (
<div className="items">
{items.map((item, index) => (
<div className="item" key={index} {...item} />
))}
</div>
) : (
<LoadingSpinner />
)
}
Display.propTypes = {
items: PropTypes.array,
}
export default DisplayItems
Did you see what I did there? I removed the implicit return from the DisplayItems
function, and then used a ternary operator to determine which component gets rendered.
If items
has any length other than 0, the ternary will evaluate to true
and thus render our items list. However, if the length of the array is 0, it will evaluate to false
and render our LoadingSpinner
component. You might end up with something similar to this:
You could, of course, use any other component that you would prefer to render instead. You can determine what’s best for your project, and even what’s best per component. But if you’re looking for a way to indicate to your users that you’re expecting data to eventually make it to your stateless functional component, perhaps this pattern will be useful to you.
Let me know if this is helpful to you in the comments and link to any examples you have of this trick or similar ones. I’d love to see them.