Build a Simple App with React.js

 


Build a Simple App with React.js 


 

What is React? 


The React Documentation defines React as a: 

“JavaScript library for building user interfaces.” 

But what does library mean in the software engineering world? We often hear the term framework used to describe code written by other developers to solve problems and simple tasks. 


To specify the difference, a framework has more control over your app; it paints the larger picture upon which your application lives and dictates the architecture of the project. 


On the other hand, a library oftentimes has a narrower, primary use case, where the programmer can link their application to gain access to the functionality. 


React works great for certain things — like building beautiful websites and setting up web applications fairly quickly — but is perhaps not the best option for other things, like building games. 


React enables developers to build creative and interactive UI’s for relatively complex applications, which require multiple, reusable components to be rendered on the page. 


Conversely, React adds less value when building a game where the logic mainly lives inside one component. 


Declarative, Component-Based, and Reusable 

 

React has three main characteristics: 


  • It uses declarative views. 

  • It is built on components that manage their own states. 

  • Its code can be reused for different browsers and technologies. 


Let’s see what this means in more detail. 


Declarative 


There are two types of programming — imperative and declarative. 


Imperative programming defines specific steps to achieve the desired results (how to do things). In contrast, declarative programming expresses the logic and data flow without explicitly listing the commands or steps (what to do). 


Let’s take making a burrito as an example. 


If we were trying to make a burrito through an imperative method, we would first get our tortilla, chop the vegetables, cook the meat, grate the cheese, assemble all the ingredients on top of the tortilla, roll it up, and serve! 


However, a declarative method would be more like how Chipotle functions. The customer doesn’t need to go through each individual step of constructing a burrito; you just specify what kind of vegetables, protein, and cheese you want, and the burrito is made for you just from those inputs. 


Because of its use of declarative programming, React applications make the code more predictable and easier to debug. Additionally, the application will know when to re-render the page, based on the changes in your data or the input. 


Components 


React applications are formed by components, which are essentially reusable building blocks for a web page. 


To get a better understanding, let’s take a look at the Airbnb home page, which is built on React. 

 
 


The diagram above represents a rough structure of what the Airbnb components might look like. We have a Nav Bar component up top which contains children components, such as the Search Bar and User Controller Bar (“Add listing”, “Messages”…etc). 


Right below the Nav Bar component, you might have a Main Container component that acts as a parent to its children components, such as the Category Container (“What can we help you find?” section) and an Ad Container (“Introducing Airbnb Luxe” section). 


 


If you take a look inside what we refer to as the Category Container, this component contains multiple children, showing individual categories (“Stays”, “Experiences”, “Adventures”, and “Restaurants”). 


These individual cards almost look identical, apart from the information shown on each card. This is where components in React become useful. 


If you write a single component of the Category Card, you can reuse the same component but provide different information accordingly. 


Conceptually, components are somewhat like JavaScript functions that render different outputs depending on the input. The output, in this case, is the HTML that gets rendered on the page. 


Another important concept with components is the state. 


Certain components manage their own state, which holds information dictating what the page will render. 


Similar to variables declared within a function, the state only applies to the component it belongs to, unless purposefully passed down to other children components as props. 


States also act as our single source of truth, meaning the component’s information will only be held within its state and nowhere else. 


We call information that gets passed down from a parent component down to its children a prop. You can think of props as analogous to function parameters in JavaScript. 


Props can take the form of any JavaScript data type— from a simple string to a function. The programmer decides whether or not to pass down a prop, depending on what needs to be rendered or called on as functions in the children components. 


The important thing to remember here is that props can only be passed down, not up. Like a waterfall, the prop can drop down but cannot defy gravity to move upwards. 


Reusable 


As React code is written in the same way for all browsers and all technologies (laptop, mobile, tablet…etc.), React applications can be programmed across multiple facets and platforms. 


For instance, developers can easily build mobile applications using React Native, a mobile application framework using React. 


With the increasing importance of web responsiveness and compatibility amongst browsers and devices, React’s reusable code becomes extremely handy. 


Building a Simple Task Application 

 

Now that we have a foundational understanding of React, let’s go through the steps of building a simple React application. For the purpose of this exercise, I decided to build a simple task-tracker app. 


Step 1. Create the React app on your terminal 

 

On your terminal, run the following command. This will create a new directory (my-app) inside the current folder. 


Within this newly generated directory, the command automatically creates an initial project structure and installs the transitive dependencies. Amongst the multiple folders created, your Javascript code will live in src. 


npm init react-app my-app 

 

Step 2. Start up the React app 


Go into your newly generated directory (my-app) and run npm start to fire up your application. 


This should automatically open up an HTML page on your browser, which will look like the page below. 


cd my-app 
npm start 

 

 


I haven’t written any code and we already have a functioning site! 


If we look into the src folder in our my-app directory, you will find a file called App.js. 

Upon the initialization of the application, this file defaults to the HTML code shown below that gets rendered on the homepage. 


 


Conventionally, App.js acts as the highest level component in the React application structure. 


Under the same folder, you also find the index.js file, which holds your application’s configuration and incorporates dependencies such as React-Router and React-Redux (more on this in a separate blog post.) 


Step 3. Set up component structure 

 

At this point, it’s time to determine which components you would like for your application. Below, I’ve laid out the component structure for my task-tracker app. 

 


In React, you write components as either JavaScript functions or classes. 


Functional (also referred to as presentational) components usually render simple HTML and do not have their own states. 


On the contrary, class components have single or multiple states of their own and could also have what is called lifecycle methods. 


Usually, the two types are separated by respective folders in the application. The developer can choose how to organize the files, however, this convention makes it easy to visualize the general component hierarchy. 


 

File organization structure sample 

 

Step 4. Set up the back end using the Rails API 

 

For the back end of this project, I created two models, Cards and Lists, using Ruby on Rails. Cards have many Lists, and a List belongs to a Card (a simple has-many-belongs-to relationship model). 


I won’t go into too much detail on setting up the back end here but for reference, see below the command line code. 


rails new my-app-api --api 
rails g resource card title 
rails g resource list description completed:boolean card:references 


With the basic back-end structure established, I now create the appropriate controller methods for both Card and List models. 


For the Rails API, remember to install rack-cors (middleware for handling cross-origin resource sharing. In other words, for your front end to be able to access the API generated by the back end. 


Install active-model-serializer if you are using it to organize your JSON data. 


//Gemfile 
gem 'rack-cors' 
gem 'active_model_serializers' 

bundle install 

 

Step 5. App component 

 

As mentioned previously, the App component will be the highest level of my application’s component tree. 


If you refer back to my component hierarchy diagram, the App renders two children components: MainContainer and Nav. 


Let’s first write out these “branches” under App.js. 


 

App.js 


The return value of my App function looks like HTML…! 


This is actually called JSX — a syntax extension of JavaScript, used in React to describe what the UI should look like. 


JSX essentially allows the developer to write HTML code mixed in with JavaScript syntax. The elements nested within the div tags (<Nav /> and <MainContainer />) are the component children that we want the App component to render. 


Components only return a single element, meaning no matter how many elements you have within your return value, the elements must be wrapped under a single element tag. 

Here, I wrap the Nav and MainContainer components under a single div tag. 


The import and export indicated at the top and bottom of the file, respectively, allow the access to the components between the different files, and therefore between components. 


In the App component, we need access to the Nav and MainContainer components, so we import them directly from their relative paths. Similarly, we export the App component so that the index.js file has the access to render the entire application. 


Step 6. Nav component 

 

I only need the Nav component for presentation, containing a logo for my application. Therefore, I made the Nav component a functional component. 


 

Nav.js 


And just like that, the site refreshes automatically with my new navbar, replacing the default React homepage. 


 


Step 7. CreateCard component — controlled form 

 

With a simple Nav component that does not need to inherit any logic (it will only render a logo), we do not need to hold the overall application logic inside the App component.

 

When we look again at our component hierarchy, the MainContainer component seems to be the rational place to hold most of the logic and state for the whole application to be passed down as props to its children. 


Hence, the MainContainer component should be set up as a class component. 


Eventually, we want the MainContainer to make a fetch request to the back end, retrieve all the cards that have been created, and pass down the cards to the ToDo Container to be rendered. 


But right now, we haven’t created any cards yet! So, let’s work on the CreateCard component first. 


As its name suggests, the CreateCard component renders a simple form which will take in a user input of title, and sends the input to the back end to create a new instance of a Card. 


There are two types of forms in React — controlled and uncontrolled. 


The main difference is that in controlled forms, the React component handles the form data, whereas, in uncontrolled forms, the DOM handles the data. 


As the CreateCard component renders a simple form, we can use an uncontrolled form. However, it is a good habit to get into writing controlled forms, as it allows for more flexibility and additional features when building a more elaborate, larger-scale application. 


A controlled form requires the component rendering the form to hold its own state to keep track of the changing input. Therefore, we will use the class component to build the CreateCard component. 


The input will be saved inside the component’s state. Each time the input changes, the CreateCard component’s state will reflect the updated input. 


To do this, we first set the state as an empty string input. We also need a function within the CreateCard class to handle the changes from the input. 


state = { 
input: "" 
} handleInput = (event) => { 
event.persist() 
this.setState({ 
input: event.target.value 
}) 
} 


As React uses event pooling, meaning the properties of an event only exist when the callback is active, event.persist() allows us to reference the event to grab its associated values and methods inside the function. 


Here, we need the event.target.value to capture the input typed inside the form. this.setState is a React method that literally sets the new state to whatever is passed in as an argument. 


The handleInput function resets the state of the CreateCard component to the new input every time the form recognizes an update. 


The last thing we need is to insert an event listener inside of the rendered form to link the form to the handleInput function. 


 

CreateCard.js 


Notice that the JSX is using both HTML-like code, along with pure JavaScript, wrapped inside curly brackets {}. 


Within the input tag, we have an event listener called onChange which points to the reference of the handleInput function we wrote above. This keyword points to the parent object, which, in this case, is the entire Create Card class. 


We only indicate the reference to the handleInput function, only to be called on when there is a change in the form input. 


When we console.log out the component’s state, we can observe that every time the user types in an input, the state updates. 


 


Finally, we need another event handler for when the user actually submits the form. Let’s take a look again at our component tree. 

 

As we will pass the input from the CreateCard component to the back end to create a new Card instance, and ultimately adding this new card to be rendered under the ToDoCardContainer, we need this entire logic to live under the MainContainer component, which is the lowest common ancestor between the CreateCard component and the ToDoCardContainer. 


Step 8. Main Component — creating a new card 

 

As mentioned above, we would ultimately want the MainComponent to keep track of all the cards from our back end to be rendered on the page. 


The first thing we will do is to set the state as an empty array of cards. 


state = { 
cards: [] 
} 


Next, we will write the createNewCard function, that will be eventually passed down as a prop to the CreateCard component. 


This function is responsible for fetching the submitted input to our back end, and receiving back the newly created card object, and adding that object to our cards array inside of the state. 


 

App.js 


Now, we pass down the reference to this function as a prop to the CreateCard component. 


 

App.js 


Notice that we do not invoke the createNewCard function. We simply pass down the reference to the function, to be called on when a user submits the form. 


Back inside our CreateCard component, we will write another function to first prevent the default action of a submit button, and call the createNewCard method, passed down from the MainContainer component. 


We access all props using this.props for class components, and simply props for functional components (making sure to pass down props as the function’s argument). 


As our input is saved under the CreateCard component’s state, we simply pass in the state as the argument for the createNewCard function. 


 

CreateCard.js 

 

Step 9. Fetching data from the back end — lifecycle methods 

 

Now that we can create cards, let’s render them on the page! 


If we refer back to our component hierarchy, the MainContainer renders the ToDoCardContainer, which in turn renders individual ToDoCard components. 


First, we need to create a fetch request to the back end to get all the existing cards, which we will eventually pass down to the ToDoCardContainer. 


Remember that a fetch request is asynchronous. If we try to render the cards before the fetch request has returned, we will get an error and the site will fail to load. 


This is where React’s component lifecycle methods come in to play. 


Using the method componentDidMount, we are able to run an asynchronous event after the initial render of the page. Once the fetch request is finished, the this.setState will trigger a re-render and will show the appropriate data returned from the fetch request onto the webpage. 


 

App.js 


Once we have all the cards’ instances saved inside of the MainContainer’s state, we will pass it down to ToDoCardContainer as a prop. 


 

App.js 


Step 10. ToDoCardContainer 

 

The ToDoCardContainer component is solely responsible for rendering all the ToDoCard components. Therefore, we will make this a functional component. 


We have all the cards passed down as props, so we can map over props.cards to create a ToDoCard component for each card object. 


As the ToDoCard component will need information on each card (namely the title of the card and, eventually, the lists associated with each card), we pass down the card object as a prop. 


It is also a good idea to pass down a key prop, using the card’s unique ID to keep track of all our ToDoCard components. 


 

 


Step 11. ToDoCard — rendering the cards 

 

First, let’s see that we can render newly created cards on the page. In the ToDoCard component, we return a div containing the card object’s title, that was passed down as a prop from the ToDoCardContainer. 


 

ToDoCard.js 


Cool! The cards are now rendered on the page. 


 


Step 12. ToDoCard — adding lists to a card 

 

Now that we were able to render the individual cards on the DOM, let’s add another controlled form on each card to allow the user to add a list item per card. 


We will use the same format of a controlled form as we did in the CreateCard component. The input state will live under the ToDoCard but the submit function will be passed all the way down from the MainContainer. 


First, we will write the controlled form inside the ToDoCard. 


 

ToDoCard.js 


The addList callback function should create a new List instance using the input (description) and the associated card ID. 


Once the new list is created in the back end and is returned to the front end, we then need to add this new list object to the appropriate card’s array of lists inside the cards state. 


To do so, we first find the associated card using the returned list object’s card_id. 


const foundCard = {...this.state.cards.find(card => card.id === cardId)} 


Then, we concatenate the new list object onto the existing lists array for that card. 


foundCard.lists = [...foundCard.lists, newList] 


We then map over the existing cards array stored in the state to essentially replace the card object in question with the new, updated card object with the additional list item. 


const newCards = this.state.cards.map(card => { 
if (card.id === cardId){ 
return foundCard 
} else { 
return card 
} 
}) 


Finally, we setState of the cards to the new cards array. 


this.setState({ 
cards: newCards 
}) 


 

MainContainer.js 

 

Step 13. ToDoList — rendering lists per card 

 

Great! Now we can add lists to our cards. 


Let’s see if we can render the lists underneath the appropriate cards. 


Just like we did before to render individual cards, we map over the lists for each card (passed down as a prop) to render a ToDoList component. 


 


Let’s see if this worked… 

 

Awesome! The list is rendered under the appropriate ToDoCard! 


Step 14. Checking off a list item 

 

As my final functionality, I want the user to be able to click on a list item and check off the list if it has been completed. 


The list items have an attribute of completed, which is set to false upon initialization. We want to update this boolean to true whenever the item is clicked on. 


As we are changing the attribute of a List object, which is kept track of inside the MainContainer’s state, we will once again write the function inside our MainContainer component. 


The function we are writing will take in both the List ID and the Card ID as its arguments. 


We first must determine if the clicked list item’s attribute of completed is true or false. If false, we want to change the status to true, and vice versa. 


We then pass this new state to update (or send a PATCH request) our database. 


 

MainContainer.js 


Once the back end updates and returns the new list object, we have to update our card’s state to reflect the change. Here, we use a similar map technique to replace the updated object from the state’s array. 


However, as the list is a nested array within the state, we need to first create a new array of updated lists, then update the card array. 


 

MainContainer.js 


We can pass down this handleClickList function down to our ToDoContainer, then down to the ToDoCard, and finally down to our ToDoList. 


This way, each ToDoList component has the handleClickList function as its prop and can call the function upon each event. 


 

ToDoList.js 


Inside the h3 tag containing the ToDoList description, I used the ternary operator to add some styling to indicate whether the list has been completed or not. 


If the list’s attribute of completion is true, then the className for the h3 tag will be completed-list — showing a tick mark and crossing out the description. 


Otherwise, the className will be to-do-list with no additional styling. 

Let’s see this in action. 

 

And there you have it! A simple task-tracker app. 


Conclusion 

 

While this article is far from a full React guideline, introduce the central pillars of React and to try and build a simple application from scratch using the library. 


Creating something on your own and getting your hands dirty is the best way to learn. Good luck! 

Comments

Popular posts from this blog

Oracle Database Server Architecture: Overview

Oracle E-Business Suite (EBS) - Introduction

Advantages and Disadvantages of React Native