Book Image

Hands-On Design Patterns with React Native

By : Mateusz Grzesiukiewicz
Book Image

Hands-On Design Patterns with React Native

By: Mateusz Grzesiukiewicz

Overview of this book

React Native helps developers reuse code across different mobile platforms like iOS and Android. This book will show you effective design patterns in the React Native world and will make you ready for professional development in big teams. The book will focus only on the patterns that are relevant to JavaScript, ECMAScript, React and React Native. However, you can successfully transfer a lot of the skills and techniques to other languages. I call them “Idea patterns”. This book will start with the most standard development patterns in React like component building patterns, styling patterns in React Native and then extend these patterns to your mobile application using real world practical examples. Each chapter comes with full, separate source code of applications that you can build and run on your phone. The book is also diving into architectural patterns. Especially how to adapt MVC to React environment. You will learn Flux architecture and how Redux is implementing it. Each approach will be presented with its pros and cons. You will learn how to work with external data sources using libraries like Redux thunk and Redux Saga. The end goal is the ability to recognize the best solution for a given problem for your next mobile application.
Table of Contents (13 chapters)

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.