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)

Stateless and stateful components

First of all, let's look at the first stateless component that has been created for us. It has been automatically generated by Create React Native App (CRNA) for our Hello World application. This component was created using the class syntax that was introduced in ECMAScript 2015 (ES6). Such components are usually called class components:

// src/ Chapter 1/ Example 1_Hello World/ App.js

export default class
App extends React.Component {
render() {
return (
<View style={styles.container}>
<Text>Hands-On Design Patterns with React Native</Text>
<Text>Chapter 1: React Component Patterns</Text>
<Text style={styles.text}>You are ready to start the journey.
Fun fact is, this text is rendered by class component called
App. Check App.js if you want to look it up.</Text>
</View>
);
}

}

Class components can be used to create stateful components.

The code samples provided in this book use ECMAScript 2018 syntax with Stage 3 feature class field declarations. Babel is the transpiler that supports such code by relevant plugins that are pre-configured for us by the CRNA toolbox. If you decide not to use CRNA, then you may need to configure Babel yourself.

However, in this case, the class component is unnecessary. We can safely use a stateless one, as it's simpler. Let's see how we can declare a stateless component. The most common approach is by using ES6 arrow syntax. Such components are called functional components. Check out the following code to see what our rewritten component looks like:

const App = () => (
<View style={styles.container}>
<Text>Hands-On Design Patterns with React Native</Text>
<Text>Chapter 1: React Component Patterns</Text>
<Text style={styles.text}>You are ready to start the journey. Fun
fact is, this text is rendered by Functional Component called
App. Check App.js if you want to look it up.</Text>
</View>
);
export default App;

If you are not a fan of arrow syntax, you can also use regular function syntax:

// src/ Chapter 1/ Example_2_Functional_Components/ App.js

export default function
App() {
return (
<View style={styles.container}>
...
</View>
);
}

The very first question that pop ups is: why is it stateless? The answer is simple: it doesn't contain any inner state. This means that we are not storing any private data inside it. Everything the component needs to render itself is provided from the external world, which the component does not care about.

In this little example, we actually never pass any external data to the component. Let's do that now. To do so, we will create another component called HelloText that consumes one property: text to display. The usual convention to pass the text to such a component is to place the text between the opening and closing tag, for instance, <HelloText> example text that is passed </HelloText>. Hence, to retrieve such a prop within our functional component, we will need to use a special key called children:

// src/ Chapter 1/ Example_3_Functional_Components_with_props/ App.js

const
HelloText = ({children, ...otherProps}) => (
<Text {...otherProps}>{children}</Text>
);
const App = () => (
<View style={styles.container}>
<HelloText>
Hands-On Design Patterns with React Native
</HelloText>
<HelloText>Chapter 1: React Component Patterns</HelloText>
<HelloText style={styles.text}>
You are ready to start the journey. Fun fact is, this text
is rendered by Functional Component called HelloText.
Check App.js if you want to look it up.
</HelloText>
</View>
);
export default App;

Using the children prop makes our HelloText component way more powerful. Props are a very flexible mechanism. Using props, you can send any valid JavaScript type. In this case, we have sent just text, but you can send other components, too.

It's time to add some vitality to our component. We will make it expand the third text block, but only after pressing the chapter or title text. For this functionality, we need to store a state that remembers if the component is expanded or collapsed.

Here is what you need to do:

  1. Change the component to the class syntax.
  2. Leverage the state object of the React library. We must initialize the state within the class constructor and make the text collapsed by default.
  3. Add conditional rendering to the component render function.
  4. Add the press handler, which will change the state once we tap on the title or chapter text.

The solution is presented in the following code:

// src/ Chapter 1/ Example_4_Stateful_expandable_component/ App.js

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

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

render = () => (
<View style={styles.container}>
<HelloText onPress={() => this.expandOrCollapse()}>
Hands-On Design Patterns with React Native
</HelloText>
<HelloText onPress={() => this.expandOrCollapse()}>
Chapter 1: React Component Patterns
</HelloText>
{
this.state.expanded &&
<HelloText style={styles.text}>
You can expand and collapse this text by clicking
the Title or Chapter text. Bonus: Check Chapter 4
to learn how to animate expanding andcollapsing.
</HelloText>
}
</View>
);
}

Congratulations—we have made our first stateless and stateful components!

Note the && operator that displays the component. If a Boolean value on the left side of the operator is true, then the component on the right-hand side will be displayed. The whole expression needs to be wrapped into curly brackets. We will explore more of its capabilities in Chapter 3, Style Patterns.

It's time to create something more challenging: Task list. Please start over and prepare your code. Clean up App.js so that it only includes the App class component:

  1. The constructor should initialize the task list in its state. In my example, the task list will be an array of strings.
  2. Iterate over the tasks to create the Text component for each task. This should happen in the render function of the App component. Please note that you can simplify iteration by using the map function instead of a regular for loop. Doing this should become second nature, since it's became a standard in almost every JS project.

My solution is presented in the following code:

// src/ Chapter 1/ Example 5_Task_list/ App.js

export default class App extends React.Component {
constructor() {
super();
// Set the initial state, tasks is an array of strings
this.state = {
tasks: ['123', '456']
}
}

render = () => (
<View style={styles.container}>
{
this.state.tasks
.map((task, index) => (

<Text key={index} style={styles.text}>{task}</Text>
))

}
</View>
);
}

Iterating using map is a nice feature, but the whole component doesn't look like a task list yet. Don't worry, you will learn how to style components in Chapter 3, Style Patterns.

What are the advantages of stateless components?

It may seem tempting to only use stateful class components and develop a whole application like that. Why would we even bother with stateless functional components? The answer is performance. Stateless functional components can be rendered faster. One of the reasons why this is the case is because stateless functional components do not require some of the life cycle hooks.

What are life cycle hooks? React components have life cycles. This means that they have different stages like mounting, unmounting, and updating. You can hook each stage and even sub stage. Please check the official React documentation to see the full list of available life cycle methods: https://reactjs.org/docs/state-and-lifecycle.html.
These are useful to trigger fetching data from the API or to update the view.

Please note that if you are using React v16 or later, it is not true that functional components are wrapped into class components internally within the React library:

"Functional components in React 16 don't go through the same code path as class components, unlike in the previous version where they were converted to classes and would have the same code path. Class components have additional checks that are required and overhead in creating the instances that simple functions don't have. These are micro-optimizations though and shouldn't make a huge difference in real-world apps – unless your class component is overly complex."

- Dominic Gannaway, engineer on the React core team at Facebook (https://github.com/reactjs/reactjs.org/issues/639#issuecomment-367858928)

Functional components are faster, but in most cases are outperformed by class components extending React.PureComponent:

"Still, to be clear, they don't bail out of rendering like PureComponent does when props are shallowly equal."

- Dan Abramov, co-author of Redux and Create React App, engineer on the React core team at Facebook (https://twitter.com/trueadm/status/916706152976707584)

Functional components are not only more concise, but they usually are also pure functions. We will explore this concept further in Chapter 9, Elements of Functional Programming Patterns. Pure functions provide a lot of benefits, such as a predictable UI and easy tracking of user behavior. The application can be implemented in a certain way to record user actions. Such data helps with debugging and reproducing errors in tests. We will dig into this topic later on in this book.

Component composition

If you have learned any Object-Oriented (OO) language, you may have used inheritance extensively. In JavaScript, this concept is a little bit different. JavaScript inheritance is based on prototypes, and so we call it prototypal inheritance. Functionalities are not copied to the object itself—they are inherited from the prototype of the object and possibly even through other prototypes in the prototype tree. We call this a prototype chain.

However, in React, using inheritance is not very common. Thanks to components, we can embrace another pattern called component composition. Instead of creating a new class and inheriting from the base class, we will create a new parent component that will use its child component to make itself more specific or more powerful. Let's look at an example:

// src/ Chapter 1/ Example_6_Component_composition_red_text/ App.js

const
WarningText = ({style, ...otherProps}) => (
<Text style={[style, {color: 'orange'}]} {...otherProps} />
);

export default class App extends React.Component {
render = () => (
<View style={styles.container}>
<Text style={styles.text}>Normal text</Text>
<WarningText style={styles.text}>Warning</WarningText>
</View>
);
}

The App component is being built out of three components: View, Text, and WarningText. It is a perfect example of how one component, through composition, can reuse the capabilities of others.

The WarningText component uses composition to enforce the orange text color in the Text component. It makes the generic Text component more specific. Now, we can reuse WarningText in any place of the app where it is necessary. If our app designer decides to alter the warning text, we can quickly adapt to the new design in one place.

Note the implicit pass of a special prop called children. It represents the children of the component. In Example 6_ Component composition - red text, we first pass warning text as children to the WarningText component and then using the spread operator it is passed to the Text component, which WarningText encapsulates.

Composing the application layout

Let's suppose we have to create a welcome screen for our application. It should be divided into three sections—header, main content, and footer. We would like to have consistent margins and styling for both logged and anonymous users. However, the header and footer content will differ. Our next task is to create a component that supports these requirements.

Let's create a welcome screen that will use a generic component for encapsulating an app layout.

Follow this step-by-step guide to do so:

  1. Create the AppLayout component that enforces some styling. It should accept three props: header, MainContent, and Footer:
const AppLayout = ({Header, MainContent, Footer}) => (
// These three props can be any component that we pass.
// You can think of it as a function that
// can accept any kind of parameter passed to it.

<View style={styles.container}>
<View style={styles.layoutHeader}>{Header}</View>
<View style={styles.layoutContent}>{MainContent}</View>
<View style={styles.layoutFooter}>{Footer}</View>
</View>
);
  1. It's now time to create placeholders for header, footer, and content. We have created three components: WelcomeHeader, WelcomeContent, and WelcomeFooter. If you wish, you can extend them to be more complex than a trivial piece of text:
const WelcomeHeader = () => <View><Text>Header</Text></View>;
const WelcomeContent = () => <View><Text>Content</Text></View>;
const WelcomeFooter = () => <View><Text>Footer</Text></View>;
  1. We should connect AppLayout with our placeholder components. Create the WelcomeScreen component, which will pass placeholder components (from step 2) down to the AppLayout as props:
const WelcomeScreen = () => (
<AppLayout
Header={<WelcomeHeader />}
MainContent={<WelcomeContent />}
Footer={<WelcomeFooter />}
/>
);
  1. The last step is going to be creating the root component for our app and adding some styles:
// src/ Chapter 1/ Example_7_App_layout_and_Welcome_screen/ App.js

// root component
export default class
App extends React.Component {
render = () => <WelcomeScreen />;
}

// styles
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 20
},
layoutHeader: {
width: '100%',
height: 100,
backgroundColor: 'powderblue'
},
layoutContent: {
flex: 1,
width: '100%',
backgroundColor: 'skyblue'
},
layoutFooter: {
width: '100%',
height: 100,
backgroundColor: 'steelblue'
}
});

Please note the use of StyleSheet.create({...}). This creates a style object that represents our app styles. In this case, we have created four different styles (container, layoutHeader, layoutContent, and layoutFooter) that will be available to use with the markup we defined. We previously customized styles using keys such as width, height, and backgroundColor, which are trivial. In this example, however, we also use flex, which comes from the term flexbox pattern. We will explain this approach in detail in Chapter 3, Style Patterns, where we focus primarily on StyleSheet patterns.

This is pretty good. We have made a trivial layout for our application and then created the welcome screen with it.

What about component inheritance?

"At Facebook, we use React in thousands of components, and we haven't found any use cases where we would recommend creating component inheritance hierarchies."
- React official documentation (https://reactjs.org/docs/composition-vs-inheritance.html)

I have not come across a situation where I had to step away from component composition in favor of inheritance. Neither have developers at Facebook (as per the preceding quotation). Hence, I highly recommend you get used to composition.