DEV Community

Mauricio Trejo
Mauricio Trejo

Posted on

From Zero To React Native Hero PT2: Building our app's coreπŸ’₯

At the first part of this guide we figured how to run our very first React Native application and learned about some fundamental concepts for building our user interfaces. You can check it here: Running our first RN app.

As said in this post, this full guide will cover these:

  • Initial React Native project configuration
  • React Native Navigation
  • Redux
  • Firebase Firestore

First step is already completed, as we built our ReactNativeNotes project and deployed it to our devices for the first time. Now we need to understand how or initial project is configured in order to finally start the coding part.πŸ€“

We'll work this guide to get this:

Demo

For starters, it's extremely important to understand how our project is configured. With our initial configuration, our root directory looks like this:
Alt Text

We can see we have three initial folders inside:

πŸ—‚ ReactNativeNotes
 πŸ“ _tests_
 πŸ“ android
 πŸ“ ios
 πŸ“ node_modules
Enter fullscreen mode Exit fullscreen mode

🧐 Important note: Both ios and android folders are projects by theirselves, android folder can be opened in android studio. For ios we can open .xcodeproj or .xcworkspace in Xcode. I recommend to use .xcworkspace as it doesn't present problems for running our project. If this file doesn't exist yet, you need to install Pods to your iOS project.

The node_modules folder contains every package we have installed and is necessary for running our app. Its content is built after the package.json file. Every external dependency we install using a package manager like yarn or npm will be reflected in this file under the "dependecies" section. This is how our package.json looks like right now:

Next important file is index.js located at the root of our project:

This file is what the bundler will be looking for to compile our application. As we can see, the method registerComponent is used to define what will be compiled, receiving as first parameter the name of our application and as second parameter our main component, which in this case is our App component imported from our App.js file.

Once these things are noted, we'll get our hands on the code!πŸ‘¨β€πŸ’»

Coding! πŸ’»

First thing will need to do is creating a new directory at the root of our project, we'll name it src. Inside this folder we will create three more folders, the first one will be named components, the second one will be named screens and the last one will me named utils. Our new project structure should look like this:

Alt Text

We did this in order to organize our project classifying which components will serve as screens and which ones will be to build a specific part of our UI.

Let's start with our screens. Create a new folder inside src/screens/ named notes. Here you'll create two files: index.js and styles.js

Alt Text

Now we are finally going to start typing. At index.js you'll need to add this:


import React, { Component } from 'react';
import {
    SafeAreaView,
    View,
    Text,
    StatusBar,
} from 'react-native';

const initState = {
}

class NotesScreen extends Component {
    constructor(props) {
        super(props)
        this.state = {
            ...initState
        }
    }


    render() {
        return (
            <>
                <StatusBar barStyle="dark-content" />
                <SafeAreaView>
                    <View style={{ justifyContent: 'center', alignItems: 'center' }}>
                        <Text>My notes</Text>

                    </View>
                </SafeAreaView>
            </>
        );
    }
};


export default NotesScreen;

Enter fullscreen mode Exit fullscreen mode

With this lines of code we will initiate our main screen using a class component. Now it's a good idea to take a look of the types of components we'll be using. 🧐

Components πŸ“Œ

As said before, the magic of React Native is that it is all about components. We need to understand that each of this components has its own lifecycle from the moment it is "summoned" to our screen. We'll take a quick look to the lifecycle methods we'll use:

  • constructor(): This is the first method to be executed when calling a component. Here's where we usually set all variables and also fetch all external data we'll be using within our component.
  • render(): This method is executed after constructor. It is in charge of displaying our UI.
  • componentDidMount(): This method is executed after rendered, and its execution is constantly active. It can listen to changes within our component and is regularly used to set events that will be used along the whole component lifecycle.
  • componentWillUnmount(): This method is executed when the lifecycle of our component reaches its end.

Now it's important to understand that we can use two types of components:

  • Class Components: This type of components provide an easier way to handle our lifecycles, we can use every method from a component inside this type. We'll be using this type for the our screens.
  • Functional Components: These are plain javascript functions which return a JSX structure. We'll be using them to render specific parts of our UI where we don't really need to deal with lifecycle methods.

❗️We can handle component lifecycles in Functional Components using React Hooks. You should take a look at that.

Let's change our App.js file!

App.js contains a functional component named App, which will return a JSX structure. This JSX structure is our NotesScreen from screens folder.

After doing this, your app should look like this:

App.js

Great! We've learned how to import a component and render it inside another one.πŸ€“ Now it's time to build our user interface with styles.

Go to utils folder and add two files called colors.js and globalStyles.js. We'll use this to centralize our styles in a certain location and make it easier to manage within our components, this truly helps when building large scale applications saving a lot of time of copying and pasting code:

Now let's go back to our notes screen folder and update both index.js and styles.js to the following:

πŸ“Œ A state in React is an object which belongs to our components context, it is composed by the "variables" we'll be using within this context. Its values can be modified by using setState() method.

Now our screen now looks like this:

First frames

It's looking kinda better isn't it!

Let's understand the code above:

// IMPORTS SECTION

import React, { Component } from 'react';
import {
    SafeAreaView,
    TextInput,
    View,
    Text,
    StatusBar,
    TouchableOpacity,
} from 'react-native';

// Importing Styles
import styles from "./styles";
import globalStyles from "../../utils/globalStyles";
Enter fullscreen mode Exit fullscreen mode

The first block of code is about importing everything we'll need to build our interface. We import the components we'll need from react-native to build the structure of our UI, and import styles to customize and adapt these components to our needs.

_textChangeHandler = (text) => {
    this.setState({ newNote: text })
}

<TextInput
    style={styles.input}
    onChangeText={text => this._textChangeHandler(text)}
    multiline
    placeholder={'Write your note here'}
    value={this.state.newNote}
/>
Enter fullscreen mode Exit fullscreen mode

TextInput component receives multiline prop to allow writing on multiple lines inside, an onChangeText props which receives _textChangeHandler function, this function sets newValue state value received from function's call.

_clearNoteHandler = () => {
        this.setState({ newNote: '' })
}

_saveNoteHandler = () => {
        const notes = this.state.savedNotes // create copy from current state value
        notes.unshift({ note: this.state.newNote, done: false }) // set new note to the top of the array
        this.setState({ savedNotes: notes, newNote: '', notesFlag: !this.state.notesFlag }) // set states with update values
}

<TouchableOpacity onPress={this._clearNoteHandler}>
       <Text style={styles.clearButtonText}>Clear</Text>
</TouchableOpacity>
 <TouchableOpacity
       style={this.state.newNote === '' ? styles.saveButtonDisabled : styles.saveButton} 
       disabled={this.state.newNote === ''}
       onPress={() => this._saveNoteHandler()}
       >
       <Text style={styles.saveButtonText}>Save</Text>
</TouchableOpacity>          
Enter fullscreen mode Exit fullscreen mode

We created our buttons using TouchableOpacity components, these receive onPress props to perform actions once tap on them. Clear button receives a function which sets newNote state to an empty string and save note button receives a function which adds the newNote value to the current notes and then updates states values.

❗️❗️Note: Functions inside a class component must be called with prefix "this." to be referred. The same applies to state values, this is necessary because these are part of this class component context.

Next step is showing saved notes array. We'll use Flatlist component to render our savedNotes state. Add FlatList to react-native imports at the top of our file, then add the following code at notesContainer View:

 <FlatList
     extraData={this.state.notesFlag}
     data={this.state.savedNotes}
     style={{ width: '90%' }}
     keyExtractor={(value, index) => index.toString()}
     renderItem={value => 
          <NoteComponent 
               note={value.item} doneHandler={() => this._checkDoneHandler(value.index)} 
               removeHandler={() => this._removeNoteHandler(value.index)} />
            }
/>
Enter fullscreen mode Exit fullscreen mode

Flatlist component receives some interesting props: data prop is the current data we'll be rendering, extraData prop is necessary in case the data we're currently rendering presents any changes, we update this flag to indicate Flatlist musts re render its content. KeyExtractor prop defines a unique key for each data value. renderItem prop receives a function which must return a JSX structure, in this case we'll be using a customized component which we must import at the top of the file.

We'll be able to mark our notes as done and remove them from our list, therefore we'll use these next functions to handle our note card actions:

_checkDoneHandler = (index) => {
        const notes = this.state.savedNotes
        if (notes[index].done) {
            notes[index].done = false
            notes.unshift(notes.splice(index, 1)[0])
        }
        else {
            notes[index].done = true
            notes.push(notes.splice(index, 1)[0])
        }

        this.setState({ savedNotes: notes, notesFlag: !this.state.notesFlag })
}


_removeNoteHandler = (index) => {

     Alert.alert(
         'Warning!',
         'Are you sure you want to delete this note?',
         [
           {
              text: 'No',
              style: 'cancel'
            },
            {
              text: 'Yes',
              onPress: () => {
                   const notes = this.state.savedNotes
                   notes.splice(index, 1)
                   this.setState({ savedNotes: notes, notesFlag: !this.state.notesFlag })
                 },
             },
          ],
          { cancelable: false }
        );
    }
Enter fullscreen mode Exit fullscreen mode

_checkDoneHandler function receives the selected index and checks wether it was already checked or not and changes it position in both cases. _removeNoteHandler uses another component called Alert which allows users to decide if they want to perform the action or not. If it is accepted, this function will remove the element from the position received from call.

As NoteComponent is a customized component, it will also receive customized props which will be passed at its invocation: note prop is the current data value to be rendered by our customized component, removeHandler and checkHandler are the functions already discussed above.

Let's build our NoteComponent to finally render our notes.πŸ’ͺ

Create a new folder inside our components directory named note. Inside this folder create two more files: index.js and styles.js, just like before. These files will look like this:

We use a functional component, which only return a JSX structure. Our structure is designed to build a card like this:
View Prev

In order to use those styled checkboxes we'll install a UI library called react-native-elements.

Run in terminal yarn add react-native-elements. After we've finished installing, we'll be able to use its checkboxes importing them like this:

import {CheckBox} from 'react-native-elements'
Enter fullscreen mode Exit fullscreen mode

We'll render the card and wrap our notes content inside a <Text> tag by calling the object note from props object:

<Text> {note} </Text>
Enter fullscreen mode Exit fullscreen mode

πŸ”Ž To use normal javascript inside a JSX code block, it's necessary to use { _our javascript code_ }.

Once our component is done, both index.js should look like this:

NoteComponent:

NoteScreen:

Now we're finally able to run our app getting this result:

Demo

We've covered a lot of new concepts in this guide which are the basis to build almost every user interface. Next part will be about storing our notes to our cloud database with Firestore.

Here's the link to this repo if you want to check it out. Leave a comment if you had any problems, doubts or just say hi if you want to.πŸ€“

Keep in touch for the coming parts of this guide.

Thank you for reading!!

Top comments (0)