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

Presentational components

It's time to learn how to make components reusable. For this goal, we will use the best tool in our hands: the presentational component pattern. It decouples components from logic and makes them flexible.

The presentational component is a pattern name that you will hear very often, if, later on, you decide to use the Redux library. For instance, presentational components are heavily used in Dan Abramov's Redux course.

I like to explain that the presentational component pattern is a website's world. For a long time now, there has been three leading blocks for every website: CSS, HTML, and JavaScript. React, however, introduced a bit of a different approach, that is, the automated generation of HTML based on JavaScript. HTML became virtual. Hence, you may have heard of the Virtual Document Object Model (Virtual DOM). This separation of concerns—HTML (view), CSS (styles), and JavaScript (logic, sometimes called the controller)—should remain untouched in our JavaScript-only world. Therefore, use presentational components to mimic HTML and container components for logic.

Approach this problem in the same fashion in React Native applications. The markup you write should be separated from the logic it consumes.

Let's see this in action. Do you remember Example 4_Stateful expandable component? It has one presentational component already:

const HelloText = ({children, ...otherProps}) => (
<Text {...otherProps}>{children}</Text>
);

This component does not introduce any logic and contains only markup, which is very short in this case. Any logic that can be useful is hidden within props and passed along, as this component does not need to consume it. In more complex examples, you may need to destructure props to pass them to the right components; for example, when using the spread operator above, all props that are not destructured are being passed.

But, instead of focusing on this simple example, let's start refactoring the App component. First of all, we will move the markup to the separate presentational component:

// src/ Chapter_1_React_component_patterns/
// Example_9_Refactoring_to_presentational_component/ App.js
// Text has been replaced with "..." to save space.

export const
HelloBox = ({ isExpanded, expandOrCollapse }) => (
<View style={styles.container}>
<HelloText onPress={() => expandOrCollapse()}>...</HelloText>
<HelloText onPress={() => expandOrCollapse()}>...</HelloText>
{
isExpanded &&
<HelloText style={styles.text}>...</HelloText>
}
</View>
);

Now, we need to replace the render function in the App component with the following:

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

However, if you run the code now, you will end up with an error on the HelloText press event. This is due to how JavaScript handles the this keyword. In this refactor, we pass the expandOrCollapse function to another object, and there, this refers to a completely different object. Therefore, it cannot access state.

There are a few solutions to this problem, and one is by using the arrow function. I will stick to the best approach performance-wise. It comes down to adding the following line to your constructor:

this.expandOrCollapse = this.expandOrCollapse.bind(this);

There we go; the application is fully functional, just as before. We have refactored one component into two—one presentational and one responsible for logic. Sweet.

Imagine that we had only shallow unit tests of two components.
Would we identify the problem with the this keyword?
Perhaps not. This simple gotcha may catch you in big projects, where you will be too busy to rethink every single component. Watch out and remember integration tests.

Decoupling styles

In the preceding examples, you may have noticed that styles are tightly coupled to presentational components. Why tightly? Because we explicitly include them by using style={styles.container}, but the styles object is not configurable. We cannot replace any style part with props, and that tightly couples us to the existing implementation. In some cases, this is a desired behavior, but in others, it is not.

If you are interested in how styles work, we will deep dive into patterns involving them in Chapter 3, Styling Patterns. You will also learn about the flexbox pattern from CSS and many other conventions.

You will bump into this problem if you have tried to split code into separate files. How can we fix this issue?

Let the styles be the optional prop. If styles are not provided, then we can fall back to the default values:

// src/ Chapter_1/ Example_10_Decoupling_styles/ App.js

export const
HelloBox = ({
isExpanded,
expandOrCollapse,
containerStyles,
expandedTextStyles
}) => (
<View style={containerStyles || styles.container}>
<HelloText onPress={() => expandOrCollapse()}>...</HelloText>
<HelloText onPress={() => expandOrCollapse()}>...</HelloText>
{
isExpanded &&
<HelloText style={expandedTextStyles || styles.text}>
...

</HelloText>
}
</View>
);

Notice the use of the || operator. In the preceding example (expandedTextStyles || styles.text), it first checks if expandedTextStyles is defined and if so returns that value. If expandedTextStyles is undefined, then it return styles.text, which is a default style object that was hard-coded by us.

Now, if we wish, in some places, we can override our styles by passing respective props:

render = () => (
<HelloBox
isExpanded={this.state.expanded}
expandOrCollapse={this.expandOrCollapse}
expandedTextStyles={{ color: 'red' }}
/>
);

This is how we split markup, styles, and logic. Remember to use presentational components as often as possible to make your features truly reusable across many screens/views.

If you come from a backend background, you may quickly jump into assumptions that it is just like the MVC pattern: Model, View, and Controller. It is not necessarily 1:1 relation, but in general, you may simplify it to the following:
  • View: This is a presentational component.
  • Model: This is a data representation, which in our case is the state that is built either in a stateful component or using so-called store and reducers (check Chapter 5, Store Patterns, to learn more details about what Redux is and how to use it).
  • Controller: This is a container component that is responsible for application logic, including event handlers and services. It should be lean and import logic from the respective files.
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 $19.99/month. Cancel anytime