How to modularize React component logic

Eleni Chappen
20spokes Whiteboard
2 min readAug 4, 2017

When learning React, I found myself copying a lot of logic from one component to another. Copying code is typically a code-smell, but I initially resigned to the idea that this was just the “React Way,” since state was limited to the component it was defined in. But no! React has ways of dealing with this too.

A good case-in-point is a recent project at 20spokes, a React Native tablet app. This app needs to update its layout in certain views, depending on whether a device is in Landscape and Portrait mode.

For orientation handling we used a library called react-native-orientation, which provides useful functions for retrieving the current orientation and detecting when orientation changes. Using this library, I could detect changes and set the current orientation in state, like so:

import Orientation from 'react-native-orientation'// ...constructor(props) {
super(props)
this.state = {
orientation: Orientation.getInitialOrientation()
}
}
componentDidMount() {
Orientation.addOrientationListener(this._orientationDidChange)
}
_orientationDidChange = (orientation) => {
this.setState({ orientation: orientation })
}
componentWillUnmount() {
Orientation.removeOrientationListener(this._orientationDidChange)
}

I kept adding this logic to every component that needed to recognize orientation, which isn’t DRY at all. So I decided to use a technique that a new team member, Jason showed us called the Function as Child pattern.

Function as Child takes advantage of the fact that this.props.children can be a function. I can create a wrapper component that holds the same logic as above, using this.props.children as a function and passing it the component’s state:

class OrientationWrapper extends React.Component {  //... same logic as above  render() <View>{ this.props.children(this.state) }</View>

}

Then I can wrap any component that needs to use current orientation. In the example below, I’m passing the state I want from OrientationWrapper to CardList by wrapping CardList in a function:

// in some other componentrender() {
<OrientationWrapper>
{ (orientationState) => (
<CardList orientation={ orientationState.orientation } />
) }
</OrientationWrapper>
...
}

The CardList component is now receiving this data as a prop and can use it however it needs to:

// in CardListgetListStyle() {
if ( this.props.orientation === "LANDSCAPE" ) {
return Style.listLandscape
else {
return Style.listPortrait
}
}
render() <View style={ this.getListStyle() }>...

The benefit of doing it this way is that I can declutter all my other components that need access to orientation data. And if we ever need to change this logic, or change our library from react-native-orientation to something else, we only have to change it in one place.

There’s some debate in the React community over Function as Child versus Higher Order Components. Ultimately they achieve the same goal, which is to modularize component logic. Whichever pattern you choose, what’s important is that you recognize when logic needs to be DRY, and make it so.

--

--