Learning Higher-Order Components in React by Building a Loading Screen
Let me share a secret: no matter how long you’ve been doing something, you’re bound to have to relearn some part of it. I haven’t built my own higher-order component in a long time and made a lot of mistakes. Real mistakes.
Why?
The other day I was adding a simple loading indicator to screens in my new app. You’ve probably used this pattern before:
This is great. It’s simple and convenient for one screen. If you’re not used to React Native, ActivityIndicator
is one of the beautiful parts of app development — a component that exists.
When you find yourself repeating this same pattern often, its nice to see what we can do to rid ourselves from the tedious task of Copy & Paste.
What do I know?
I know that I’ll have a loading
prop FOR SURE.
What am I apprehensive about? The strategy I will use to genuinely make my life easier (instead of harder — sometimes we need a friendly reminder).
First Pass
Besides the function wrapping React.PureComponent
everything should look familiar. If you want to skip ahead and use this as-is, just paste this into our InfoScreen
file and wrap the export:
export default withLoadingScreen(InfoScreen)
This pattern may look familiar if you’ve used Apollo or Redux before:
export default connect(mapStateToProps, mapActionCreators)(InfoScreen)
Compose
If you’re wondering how to use both connect
and withLoadingScreen
you can wrap the components individually or use a compose
function. Writing a compose function by hand is cool, but importing the one from one of the libraries you’re already using is cooler. Both redux
and react-apollo
come bundled with their own:
Does it work?
Depending on what your setup looks like, it might work perfectly or it might not work at all! 😃 🙃
I’m using react-navigation
which supports a static method called navigationOptions
to override header title and styles.
The loading screen works as intended, but the header styles are gone! What happened? Static methods aren’t copied over! It’s super easy to overlook but also super easy to fix.
There’s a library called hoist-non-react-statics that will automatically copy over static variables like navigationOptions
, defaultProps
and propTypes
.
Second Pass: Hoisting Static Variables
Instead of returning the class immediately, we return it only after calling hoistNonReactStatics
. The header is rendering correctly again. Woo!
Third Pass: Passing in Options
Now that the loading screen itself functions correctly, I want to pass in a different loading indicator size for different screens.
Sometimes the default loading indicator is too big or the wrong color so I’d like to be able to adjust when I need to.
connect
and graphql
both give you options:
connect(mapStateToProps, mapActionCreators)
graphql(MyQuery, { options })
We can do the same by adding another function call:
In this case I wanted to control the size of the ActivityIndicator
withLoadingScreen('large')(InfoScreen)// or using compose:compose(
withLoadingScreen('large'),
compose(mapStateToProps, mapActionCreators)
)(InfoScreen)
Bonus Round: Naming Your Components
You’re bound to run into issues. Sometimes the component name doesn’t show up or doesn’t match the component in question. We can fix this easily by writing a simple function called getDisplayName
:
function getDisplayName(WrappedComponent) {
return (
WrappedComponent.displayName || WrappedComponent.name || "LoadingScreen"
);
}
Conclusion
Higher-order components have a few weird things about them that you can fix easily. Code responsibly.
Fun things to do:
- Follow me on Twitter.
- Check out Orchard: Stay in touch with people that matter the most and see how powerful Expo & React Native can be for building your next app.