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.
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:
- Change the component to the class syntax.
- Leverage the state object of the React library. We must initialize the state within the class constructor and make the text collapsed by default.
- Add conditional rendering to the component render function.
- 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!
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:
- The constructor should initialize the task list in its state. In my example, the task list will be an array of strings.
- 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.