Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Hands-On Design Patterns with React Native

You're reading from   Hands-On Design Patterns with React Native Proven techniques and patterns for efficient native mobile development with JavaScript

Arrow left icon
Product type Paperback
Published in Sep 2018
Publisher Packt
ISBN-13 9781788994460
Length 302 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Mateusz Grzesiukiewicz Mateusz Grzesiukiewicz
Author Profile Icon Mateusz Grzesiukiewicz
Mateusz Grzesiukiewicz
Arrow right icon
View More author details
Toc

Table of Contents (13) Chapters Close

Preface 1. React Component Patterns 2. View Patterns FREE CHAPTER 3. Styling Patterns 4. Flux Architecture 5. Store Patterns 6. Data Transfer Patterns 7. Navigation Patterns 8. JavaScript and ECMAScript Patterns 9. Elements of Functional Programming Patterns 10. Managing Dependencies 11. Type Checking Patterns 12. Other Books You May Enjoy

Container component

The container component pattern was introduced a long time ago and was popularized within the React community by Dan Abramov. So far, we have created one container component when we refactored the contents of the App component to become a presentational component. It turns out that the App component became a container component—it contains the HelloBox component and implements the necessary logic for it. What did we gain from this approach? We gained the following:

  • We can implement expanding and collapsing in a different way and reuse the markup of the HelloBox component
  • HelloBox does not contain logic
  • The container component encapsulates logic and hides it from the other components
I highly recommend reading Dan Abramov's medium post on this. Check out https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0 for more information. Container components are very useful tools when it comes to dependency injection patterns. Have a look at Chapter 10, Managing Dependencies, to learn more.

HOC

The HOC is a pattern that exists to enhance components with additional props or functionality, for instance, if you want to make the component expandable. Instead of just creating a stateful container as we did previously, we could use the HOC pattern. Let's refactor our stateful container component to a HOC and name it makeExpandable:

// src/ Chapter_1/ Example_12_Higher_order_component_makeExpandable/ App.js

const makeExpandable = (ComponentToEnrich) => (

class HelloBoxContainer extends React.Component {
constructor() {
super();
this.state = {
// default state on first render
expanded: false
};
this.expandOrCollapse = this.expandOrCollapse.bind(this);
}

expandOrCollapse() {
// toggle expanded: true becomes false, false becomes true
this.setState({expanded: !this.state.expanded});
}

render = () => (
<ComponentToEnrich
isExpanded={this.state.expanded}
expandOrCollapse={this.expandOrCollapse}
/>
);
}
);

The makeExpandable component accepts ComponentToEnrich. So, we can create a root component (App) like this:

export default makeExpandable(HelloBox);

Cool, isn't it? Now, let's create some other component and enrich it with our HOC. This will be a small button that displays the text hide or show. If the user presses the button, it should show or hide a small colored box. For this task, you can use the following styles:

box: {
width: 100,
height: 100,
backgroundColor: 'powderblue',
}

Place them within StyleSheet.create({ ... }). My solution is pretty straightforward:

// src/ Chapter_1/
// Example_13_Higher_order_component_show_hide_button/ App.js

export const
SomeSection = ({
isExpanded,
expandOrCollapse,
containerStyles,
boxStyle
}) => (
<View style={containerStyles || styles.container}>
<Button
onPress={expandOrCollapse}

title={isExpanded ? "Hide" : "Show"}
color="#841584"
/>
{isExpanded && <View style={boxStyle || styles.box} />}

</View>
);

export default makeExpandable(SomeSection);

In the preceding example, the SomeSection component is wrapped by the makeExpandable HOC, and receives the isExpanded and expandOrCollapse props.

Great! We have just made a reusable HOC, and it is working flawlessly.

Now, I will show you a rather unknown but sometimes useful technique to push your HOC to be even more flexible. Imagine that you are about to enhance a component that is strict about props naming, as in the following example:

export const SomeSection = ({
showHideBox,
isVisible,
containerStyles,
boxStyle
}) => {...};

Unfortunately, our HOC, makeExpandable, is passing the wrong prop names. Let's fix that:

// src/ Chapter_1/ Example_14_Flexible_prop_names_in_HOC/ App.js
render
= () => {
const props = {
[propNames && propNames.isExpanded || 'isExpanded']: this.state.expanded,
[propNames && propNames.expandOrCollapse || 'expandOrCollapse']: this.expandOrCollapse
};
return <ComponentToEnrich {...props} />
};

This is a tricky example. It provides a capability to rename props that are passed down by HOC. To rename it, we need to pass a configuration object called propNames to HOC. If such an object is passed, and it contains a certain key, then we override the name. If the key is not present, then we fall back to the default prop name, for instance, isExpanded.

Notice the use of [] inside of the object. It allows you to dynamically name keys in the object. In this example, the key was dynamically chosen based on the presence of propNames.

To make everything work, we also need to accept the optional argument propNames in the makeExpandable HOC:

const makeExpandable = (ComponentToEnrich, propNames) => (
...
)

Cool! Now our HOC is more flexible when it comes to prop names! We can use it with the aforementioned strict SomeSection component:

export default makeExpandable(SomeSection, {
isExpanded: 'isVisible',
expandOrCollapse:
'showHideBox'
});

Beware of the performance implications when creating variables inside the render function. It will slow your application down. Sometimes, patterns can sacrifice performance a little and sometimes they cannot. Use them wisely. You could also the inline propNames variable as two props.

Make sure to check the next section for a cleaner and decoupled approach.

HOC composition

The primary reason to create HOCs it to have the ability to compose the features they provide.

Look at the problem from the previous section again. What if we could delegate work to another HOC? For instance, having a mapper HOC called mapPropNames, you can compose it with our previous HOC like this:

makeExpandable(mapPropNames(SomeSection));

Here is the implementation of mapPropNames:

// src/ Chapter_1/ Example_15_HOC_Composition/ App.js

const
mapPropNames = (Component) => (props) => (
<Component
{...props}
isVisible={props.isExpanded}
showHideBox={props.expandOrCollapse}
/>
);

Nice and quick, isn't it? This is a common pattern and is also used when working with backend data sent as JSON. It may adapt the data format to our representation on the frontend layer. As you see, we can employ this great idea when working with HOCs as well!

If you come from an object-oriented background, please notice that the HOC pattern is very similar to the decorator pattern. The decorator, however, also relies on inheritance and needs to implement the interface that it decorates.

Please check https://en.wikipedia.org/wiki/Decorator_pattern for examples.

You can also compose decorators. It works in a similar way.

Examples of useful HOCs

Do you need a quick logger that will show you how your app behaves? Or maybe you are preparing a live presentation and you want to show some dynamic information on a screen? Here we go:

// src/ Chapter_1/ Example_16_Useful_HOCs/ App.js

const
logPropChanges = Component => props => {
console.log('[Actual props]:', props);
return <Component {...props} />;
};
// Use: makeExpandable(logPropChanges(mapPropNames(SomeSection)));

Okay, good. Now, let's suppose that you are waiting on some data to load. Here comes the spinner:

// src/ Chapter_1/ Example_16_Useful_HOCs/ App.js

import {ActivityIndicator} from 'react-native';
const
withSpinner = Component => props => (
props.shouldSpin
? <View style={styles.container}>
<Text>Your fav spinner f.in. on data load.</Text>
<ActivityIndicator size="large" color="#0000ff" />
</View>
: <Component {...props} />
);
// Needs a HOC that provides prop shouldSpin (true/false)

You might want to ask a user to five star your app. You need a modal to do this:

const withModalOpener = Component => props => (
// Replace with your favourite Modal box implementation
<Component {...props} openModal={() => console.log('opening...')} />
);

Sometimes, modals should be intelligent enough to maintain their visibility, too:

// src/ Chapter_1/ Example_16_Useful_HOCs/ App.js

const
withModalOpener = OriginalComponent => (
class ModalExample extends React.Component {
// Check this shorter way to init state
state = {
modalVisible: true,
}
;

setModalVisible(visible) {
this.setState({modalVisible: visible});
}

render() {
return (
// Replace with your favourite Modal box implementation
<View style={styles.container}>
<OriginalComponent
{...this.props}
openModal={() => this.setModalVisible(true)}
closeModal={() =>
this.setModalVisible(false)}
/>
<Modal
animationType="slide"
visible={this.state.modalVisible}
onRequestClose={() => {
alert('Modal has been closed.');
}}>
<View style={styles.container}>
<Text>Example modal!</Text>

<TouchableHighlight
onPress={() => {
this.setModalVisible(false);
}}>
<Text style={{fontSize: 30}}>
Hide Modal
</Text>
</TouchableHighlight>
</View>
</Modal>
</View>
);
}
}
);

In this example, we enriched the component with Modal. Modal can be opened or closed using the props that are named openModal and closeModal. The information regarding whether the modal is opened or closed is stored within a private state of the HOC and, in this example, is not exposed to the original component. Nice separation, right? This HOC is also reusable.

Time for your homework: how do we make Modal open along with the box show? You cannot change SomeComponent.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime