Learning to create components and styles in React Native - Part II

Article cover

Continuing our article that teaches us how to start a React Native project and how to insert external sources, today we will advance our knowledge a little further, where we will discover what the famous components are and how we can make them even more beautiful by adding styles.

I believe that from this article specifically, I will explain in a more detailed way, but if doubts still persist, I am available for them to be resolved, because when we learn a new technology, the more information we have, we seek and learn, the better to fix knowledge, with that, I hope that the articles instigate you even more to seek more about this framework that is super important and widely used.

And before we really get into the subject of React Native, I think it's super important to make our projects visible on Github, and in addition, to have this material as a source of studies and doubts. Believe me, what you will learn below will be valid throughout your learning process, and by saving our code and making it available, we create a valid source of information, which we produce ourselves, in addition to training commands and functionalities that will be used a lot in the future. work environment.

With that, let's start today by learning in an even better way, starting with Git.

Creating a new branch in our project on Github

As I suggested in the last article, I think it's valid for the project to be on Github, but don't worry, if you haven't been able to upload your project, or if you haven't completed all the steps in the previous article, in the link below you'll have access to the project that it's on my Github, and if you prefer, feel free to clone this project / perform a fork.

https://github.com/ildaneta/challenges/tree/master/toDo-app

Once you have the repository on your Github, I suggest downloading it so that today we can start creating a new branch, and for that, we will follow the naming structure below:

modificationtype/feature-changed-or-created

Which would look more or less like this:

feat/component-creation

It's nice that when creating branches, you are descriptive and clear, so that when you need to go back to that branch again, it's easy to find.

This naming pattern is what we use in our work project, and it was a consensus that my team arrived at and chose, so each team or company may have different standards for naming branches.

Another interesting point is that the feat comes from feature, following the pattern of nomenclatures that exist in Conventional Commits, where I even wrote an exclusive article about it and you can read it here: Understanding the importance of Semantic Commits.

So, assuming that our branch was to fix a bug in production, we would put the fix suffix, getting: fix/correcao-componente-input

Now that we know the nomenclature, let's start typing the command lines to enter our project, and create a new branch.

1 - cd foldername that the project is

2 - When we enter the ToDo project folder, we will type git branch to find out which branch we are currently in, it will probably be master or main:

prompt

So let's create our new branch, where we will learn about creating components and styles, I suggest the name below, but you can name it the way you find most pleasant, what I quote here are just suggestions:

git checkout -b feat/todoApp/creating-components-and-styles

The screen displayed will look like the one below:

prompt branch master

When we type the git checkout command branchname in isolation, we are just changing from the current branch to the typed branch, but when we put -b in front of it, we are creating a new branch from the current branch that we are already changing into this new branch, as shown in the message: Switched to a new branch...

Now, if you run the command line below, you will see that we are already inside this new branch:

git branch

branch

That's it, branch created and successfully verified, now we'll start our path through React Native.

Better understanding tags in React Native

Really thinking about understanding React Native starting with the fundamentals, I think it is important to say some small differences that we have in relation to ReactJS web and React Native, and the first of them is in relation to tags.

On the frontend one of the main knowledge we have to have is about the HTML tags, because when you deal with sites that will be rendered via browser and subsequently ranked, it is extremely important to understand the semantics behind the tags and how the good use of them can increase the SEO of your page, for example. However, as React Native is not rendered in the same way, we do not have tags like in the frontend, because basically in mobile we have more generic tags, such as the <Text> tag that serves to write any type of text, unlike the web that we have <p>, <strong>, which are different ways of writing texts, because each of the tags has its particularities.

React Native works as a special collection of React components, and our components/tags compile to native widgets, both native iOS and native Android, as follows:

image

But the doubt may arise, is only our interface compiled? And what happens to our Javascript code?

Our Javascript code is not compiled, because it runs in a virtual machine at the end, run and hosted by React Native inside our application. React Native also gives us tools for our Javascript code to communicate with native modules and API's, such as when we want to access the device's camera.

image

We have some tags that are used a lot in our daily lives, for example: <View>, <Text>, <Image>, <TextInput>, <ScrollView> and <TouchableOpacity> .

But so that you can better understand the tags and the tool, I leave the link to the React Native doc below:

https://reactnative.dev/docs/components-and-apis

What are components inside React Native?

Now that we understand a little more about tags in React Native and how they are compiled, it's time to understand what components are.

Starting from the React documentation, components allow you to divide the UI (User Interface) into independent and reusable parts, thinking about each part in isolation.

Conceptually, components are like Javascript functions. They accept arbitrary input (called “props” = properties) and return React elements that describe what should appear on the screen.

Basically as follows:

import React from 'react'
import { Text } from 'react-native'

function Welcome(props) {
  return <Text>Hello, {props.name}</Text>
}

export default Welcome

So, as we saw in the documentation itself, components are isolated code snippets, that is, they have their own styles and properties, which can be reused in the application.

An important observation is that a component may or may not receive properties.

In the code snippet above, we are creating a component called Welcome, and this component/function will receive a property called name. So to use this component, the code would look something like this:

import React from 'react'

import Welcome from '../../components/Welcome'

const Main = (): JSX.Element => {
  return <Welcome name="Ilda Neta" />
}

export default Main

What we are doing is basically importing this component and placing inside it the property it expects, the prop name, with the string value Ilda Neta.

Now that we understand a little more about the fundamentals, it's time to code, and with that, let's get to know the layout of our application:

ToDo app interface

As I mentioned in the previous article, it's a simple but functional layout, and what's really important is that you understand the concepts and be able to apply them later on in other projects. The colors, nomenclatures and all the rest of the code can be created according to your taste, what I provide is just an inspiration for you to keep in mind a way to create your application, with that, feel free to customize your project way that suits you best.

We will then create our first component, which will be the Header, and to create it we will follow the steps below:

  1. We will go to our components folder and create inside it another folder called header and inside the header folder we will create the files Header.tsx and styles.ts:

image

Here we come to another very cool tip, which is the difference between using the .ts and .tsx extension.

When we use the .ts extension, we are saying that this file will not have JSX code snippets, that is, tags with texts and styles linked to Javascript code. But when we use the .tsx file, we are saying that this code will have JSX snippets.

If you have any questions related to this, I leave below the explanation contained in the React doc about JSX:

UI Description - React Documentation.

  1. After creating the folder structure of the Header component, let's start writing the code, with that it is always necessary to import React and the tags that we will use from React Native. After that, we create our Header function which will be an arrowFunction that returns an empty View:
import React from 'react'
import { View } from 'react-native'

const Header = (): JSX.Element => {
  return <View />
}

export default Header

As we had started in the other article, we will be using typescript now because it brings us many advantages in writing code, one of them is the intellisense that I will show you later, but for those who have never seen a typescript, how can we identify them?

The types will usually come after the colon, and that according to the code above, we can see the type of the function as follows : JSX.Element. Previously, we used React.FC to type the returns of functions, as follows:

import React from 'react'
import { View } from 'react-native'

const Header: React.FC = () => {
  return <View />
}

export default Header

However, this typing ends up bringing some small problems in the code, with that, Facebook removed this typing from the typescript template of create-react-app. In case you want to study more about this point, I leave you with the Facebook thread below where they accept this suggestion via pull request:

https://github.com/facebook/create-react-app/pull/8177

In place of this React.FC typing, we put that the function will return a JSX.Element

Another peculiarity that we have in functions is that, in addition to typing the return of the function, as above, when this function receives parameters, we will also have to type these parameters, and we will see this situation below.

There are many details, but I hope that you will continue to persevere, as we will overcome the concepts calmly, patiently and willingly to learn.

And basically this is a component, but the way it is it won't return anything, for that, we'll add a text, which will be the header of our application, as follows:

import React from 'react'
import { View, Text } from 'react-native'

const Header = (): JSX.Element => {
  return <Text>ToDo App</Text>
}

export default Header
  1. Component created, now it's time to import it and display it in our application.

To do this, let's go to our App.tsx file and import our Header component:

import React from 'react'

import Header from './src/components/header/Header'

const App = (): JSX.Element => {
  return <Header />
}

export default App

We'll also type the return of the App function by adding the (): JSX.Element.

After that, we will run our app again. If your project has already been installed on the device, just run yarn start for the metro bundler to run, and open our app on the emulator.

But if you don't have the app installed on the emulator, run yarn android or yarn ios so it will build the application, install it on your emulator and open the app.

And our result will be:

image

As you can see, on iOS it considers the notch as part of the screen, and thus our interface is broken.

But React Native has a specific tag for this, called <SafeAreaView>, and as the name of the tag itself reveals, it helps us to display our interface only in the area that is visible to the user, with that, we will change our code this way:

import React from 'react'
import { SafeAreaView } from 'react-native'

import Header from './src/components/header/Header'

const App = (): JSX.Element => {
  return (
    <SafeAreaView>
      <Header />
    </SafeAreaView>
  )
}

export default App

We import the SafeAreaView and place it around our application, so any content rendered by the application will be in an area visible to the user.

We can also notice that our return has changed, and this is because when we render more than one tag, we have to put the () around the tags, as per the code above.

And this is the result in our application:

image

Handling styles in React Native

We were then able to create our first component, but as we can see, it is very simple, without styles and without the external font that we imported. With that, now let's learn a little more about stylizations.

We created the styles.ts file inside our Header component folder above, but how can we create the styles inside it?

import { StyleSheet } from 'react-native'

export const styles = StyleSheet.create({})

Basically, the code snippet above is the start to create our styles, where we use the StyleSheet method provided by react native.

After importing it, we need to export our styles constant, which takes StyleSheet.create({}).

To link our styles to our component, we need to go back in the Header.tsx file and import our styles, like so:

image

And to really have visual effects in our component, we put the styles property inside the <Text> tag, as follows:

import React from 'react'
import { Text } from 'react-native'

import { styles } from './styles'

const Header = (): JSX.Element => {
  return <Text style={styles.textHeader}>ToDo App</Text>
}

export default Header

All React Native tags can receive this style property, and so, within it we say which property will pass the visual identity of our component within our styles constant. As we can see, we are saying that the textHeader property will be the one that will pass styles to our text, with that, we will go back inside our styles.ts file and add this property, as follows:

import { StyleSheet } from 'react-native'

export const styles = StyleSheet.create({
  textHeader: {
    fontFamily: 'Montserrat-Bold',
    fontSize: 22,
  },
})

Resulting:

image

As it is visible in the style, in React Native it is a little different from web also in the declaration of style properties, because in mobile we do not use the font-family with a hyphen as in web, and this happens because what we type is not exactly CSS, but a styling based on CSS, and therefore the nomenclature ends up being a little different.

A good practice is also that we do not create our styles in the same application logic file, as we did in the first article, so here, we separate them into two files.

And as usual, so that you can delve deeper into the knowledge of StyleSheet, I leave below the documentation about it:

https://reactnative.dev/docs/stylesheet

Understanding Props or Properties better

So, we've just created our first component and we've even styled it, but in reality this component is still quite simple and without the final styling we need for our application, so it's time to understand a little more about the props/properties that our components can receive and how we can start typing them.

At the very beginning of the article, we saw the Welcome component, which receives a name property, and in our Header component, we will do the same thing, but our property will be called headerTitle, as follows:

import React from 'react'
import { Text } from 'react-native'

import { styles } from './styles'

const Header = (prop): JSX.Element => {
  return <Text style={styles.textHeader}>{prop.headerTitle}</Text>
}

export default Header

But we can improve this code a little more as follows:

import React from 'react'
import { Text } from 'react-native'

import { styles } from './styles'

const Header = ({ headerTitle }): JSX.Element => {
  return <Text style={styles.textHeader}>{headerTitle}</Text>
}

export default Header

What we improved in the above code is that we destructure the property from within props, it's the same as doing this:

const { headerTitle } = props;

As we already know, React Native is just a framework that helps us in the production of the application, but having a Javascript base is extremely indispensable, so I advise you to study more about destructuring, manipulation of arrays and spread and rest operators.

Even performing this destructuring, our prop is untyped, which causes the code to have this alert:

image

Basically ESLint is warning us: the headerTitle needs to be typed.

For this, we will create an interface called IHeader, the letter i that precedes the name of the typing is a good practice so that you know that this typing is of type interface, because we can have other types such as enums.

import React from 'react'
import { Text } from 'react-native'

import { styles } from './styles'

interface IHeader {
  headerTitle: string
}

const Header = ({ headerTitle }: IHeader): JSX.Element => {
  return <Text style={styles.textHeader}>{headerTitle}</Text>
}

export default Header

An interface basically serves to type items contained in objects, and in the code above we see that inside the IHeader interface we added the headerTitle property which will receive a value of type string.

This is relatively simple typing, but if this is your first time viewing it, or if you want to go deeper into the subject, I leave the content below about TypeScript:

https://www.typescriptlang.org/docs/handbook/basic-types.html

https://blog.rocketseat.com.br/typescript-vantagens-mitos-conceitos/

Now if we go back to our App.tsx file, we will see that our <Header /> component will turn red, indicating that we have an error that can be seen by hovering the mouse over the component:

image

The message tells us that this component now expects a mandatory property called headerTitle, and with that, we can supplement our component as follows:

import React from 'react'
import { SafeAreaView } from 'react-native'

import Header from './src/components/header/Header'

const App = (): JSX.Element => {
  return (
    <SafeAreaView>
      <Header headerTitle="ToDo App" />
    </SafeAreaView>
  )
}

export default App

But that's not all we need to understand more about our properties. For example, did you ask yourself why we use a prop? Couldn't we have left it with the fixed text, as it was before?

And that's where the cat jump comes in lol, when we use a property, we're usually looking for information or a characteristic that can be changed within the component. If we left our Header component the way it was before, whenever we were going to use it we would have the same text: ToDo App, because this text was fixed inside it.

Now, as we use a property, every time the Header component is to be used, it will be necessary to inform the headerTitle property, which can assume any value of the string type. With this, we managed to make our component a little more malleable to changes, without having to go inside the component to change.

Another curiosity about props is that there is a special prop called children. When we use this prop, it takes the value that is between the opening and closing of the component tag, different from when we create other properties, which go inside the tag. It seems a little complex, but this would be the difference:

Inside the Header component, the prop declaration remains the same:

import React from 'react'
import { Text } from 'react-native'

import { styles } from './styles'

interface IHeader {
  children: string
}

const Header = ({ children }: IHeader): JSX.Element => {
  return <Text style={styles.textHeader}>{children}</Text>
}

export default Header

What changes is where we use the component, when we use the children prop, the tag no longer closes on itself, but becomes a tag with opening and closing:

// Using the children prop
<Header>ToDo App</Header>

// Using the headerTitle prop
<Header headerTitle="ToDo App" />

I particularly like to use it when the tag closes on itself, as we did in our Header using the headerTitle prop.

I believe we could learn a lot today, and despite the article being longer than I thought, we will finalize today's knowledge leaving our Header component ready for the next article, and we will also add one more good practice below:

  1. We'll go back to our Header.tsx file where we'll add a <View> that will encompass our Header and we'll also add a style property inside our View:
import React from 'react'
import { Text, View } from 'react-native'

import { styles } from './styles'

interface IHeader {
  headerTitle: string
}

const Header = ({ headerTitle }: IHeader): JSX.Element => {
  return (
    <View style={styles.container}>
      <Text style={styles.textHeader}>{headerTitle}</Text>
    </View>
  )
}

export default Header
  1. Now we'll go into our styles.ts file and create our styles:
import { StyleSheet } from 'react-native'

export const styles = StyleSheet.create({
  textHeader: {
    fontFamily: 'Montserrat-Bold',
    fontSize: 22,
    color: '#fff',
  },

  container: {
    backgroundColor: '#f26',
    marginBottom: 20,
    height: 60,
    alignItems: 'center',
    justifyContent: 'center',
  },
})

When we save the files, we can already see the result in our emulator:

image

Now we're close to finishing, so it's not a good practice to assemble components and screens directly in our App.tsx file, because this is the main file of our application. With that, let's go to our screens folder and create a folder for our Main page, following the same component structure:

Main folder → create Main.tsx and styles.ts files inside it

import React from 'react'
import { SafeAreaView } from 'react-native'

import Main from './src/screens/main/Main'

const App = (): JSX.Element => {
  return (
    <SafeAreaView>
      <Main />
    </SafeAreaView>
  )
}

export default App

And our component will continue to be displayed in our application:

image

Last but not least, as we finished the code part, now comes the time to commit our code changes, and later merge this branch into our master branch.

And for that, we will follow one more step by step below.

  1. We will start by committing our changes, and since I have the GitLens extension in my VSCode, I can commit inside it, as the extension offers me a Git tab on the left side of VSCode:

image

So, when we click on a file, it makes a comparison of what existed before in that file that we want to commit, and what we put inside of it, as a git diff.

Below I leave a short video where I commit the changes to our Main Screen:

image

  1. After we've committed our files, we'll open our terminal and type the command line git push so we can actually push our local code to our git.

Git itself suggests the —-set-upstream command, which is a command that sets the default remote branch to the current branch. If you also want to delve deeper into this point, I leave the Git branch documentation below:

https://git-scm.com/book/en/v2/Git-Branching-Remote-Branches#Tracking-Branches

  1. After committing and actually sending our code to Git, we will have to merge our changes with the content contained in the master of our project. You can do this via command line or within Github.

To do it inside Github, just access the repository of our project and you will already see the following message:

image

With that, just click on the Compare & pull request button and a page will be displayed with all the changes we made, and then we can create our pull request:

image

With the pull request created, we can send it to friends who can perform a brief code review of our application. Later, a new screen will be displayed to check the commits we've made and approve the pull request for our master branch.

Once the code is merged, it will appear on the screen:

image

Now, code successfully merged, our learnings have already gone to the master branch of our application!

Making a brief summary of today's article, we were able to learn about the following topics:

  • How to create a new development branch
  • Concepts of how our interface is compiled for native widgets and how our Javascript code will end up on our device
  • Concepts of tags in React Native
  • How to create components and styles
  • How to merge our code into another branch

I' a'm immensely grateful to everyone who has made it this far and, as I always mention to you, I hope that this article awakens your curiosity even more and the desire to learn more and more.

So if you still have doubts or have any doubts that I didn't highlight in the article, don't hesitate to contact me. It'll be a pleasure to assist you!