Skip to content

Instantly share code, notes, and snippets.

@jdgo-mars
Created December 16, 2020 02:43
Show Gist options
  • Save jdgo-mars/477322a7a9ff97ef2d9f5f603b4422a3 to your computer and use it in GitHub Desktop.
Save jdgo-mars/477322a7a9ff97ef2d9f5f603b4422a3 to your computer and use it in GitHub Desktop.

Note to self: Make a script that counts the words on an article and yield avg reading time.

React Primer

A gentle intro do react

What is react?

React is a Library. What is a library you might ask?

Its a Collection of code that solves a particular problem.

In their own words:

React is a JavaScript library for building user interfaces.

So where is this code? Can I see it?

Well in react's case, yes. Because it is open source, the code lives in Github a very popular place for open source Projects. Examples of open source software are: Linux, VLC Media Player, Mozilla Firefox, among others...

Is all software open source? Short answer is no. A lot of software is closed source, that means the source code is owned by a Company, and therefore not available to the general public, examples of this are: Microsoft windows, Google Chrome, Microsoft Office, among others...

Other relevant libraries for frontend devleopment are :

  • moment.js — helps you manipulate dates with ease
  • D3.js — helps you transform data into graphics and visualizations.

One common thing between libraries is that they all possess an API, this is the way you use these libraries.

For example, for React you can see how to use it on their docs page https://reactjs.org/docs/getting-started.html

Why it exists aka what problems it solves?

There is no point in using a tool if there is no added value.

So what value does react bring into the frontend game?

Why don't we just manipulate the DOM directly with pure javascript or JQuery?

Since an image is worth a 1000 words, I will present you with a problem and two different solutions.

The old-style aproach, and the new-style react appoach.

Let's dig into it:

Pre-requisites:

Understand REST APIs and HTTP protocol

For simplicity, a REST API is a service hosted on a web server, which adheres to some rules, which provides data, usually as JSON

To interact with it, normal HTTP protocol is used, the same way you access an url to request let's say google.com .

HTTP is a very important protocol which we use all over the internet, having a good grasp on its details is crucial,

This are some great resources to learn about it:

Code Problem

Create a table with user data from a REST API.

We will use the data provided by: https://jsonplaceholder.typicode.com/users

Go ahead and open it on the browser, you can see the raw JSON data.

A single user as the following form

{
  "id": 1,
  "name": "Leanne Graham",
  "username": "Bret",
  "email": "Sincere@april.biz",
  "address": {
    "street": "Kulas Light",
    "suite": "Apt. 556",
    "city": "Gwenborough",
    "zipcode": "92998-3874",
    "geo": {
      "lat": "-37.3159",
      "lng": "81.1496"
    }
  },
  "phone": "1-770-736-8031 x56442",
  "website": "hildegard.org",
  "company": {
    "name": "Romaguera-Crona",
    "catchPhrase": "Multi-layered client-server neural-net",
    "bs": "harness real-time e-markets"
  }
}

Not all them are relevant, let's just use — id, name, username, email, phone for our use case.

1. The old-school, boring non-react way

  1. Let's create an index.html and prepare the ground

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
  <!-- 
		Our table starting point 
		an id will helps identify it
		on our js code
	-->
  <table id="usersTable">
    <!-- 
      table rows and columns will be created dynamically 
      after we fetch the data from the network 
    --> 
  </table>
	
  <script>
  	// JS Magic goes here
  </script>
    
</body>
</html>

2. Get that data 🤤

From here we will focus our atention inside the script tags

<!-- index.html -->
<!-- .... -->
<script>
	
  /**
  *	returns - a promise with json in the form of an array of 
  * users with the properties seen before
  */
  async function getUsers() {
 		// retrieve data using the fetch api
    // more info on it: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
    // the use case is simple we just need to pass the url as parameter
    // response will be of type https://developer.mozilla.org/en-US/docs/Web/API/Response
    const response = await fetch("https://jsonplaceholder.typicode.com/users");

    // reference: https://developer.mozilla.org/en-US/docs/Web/API/Body/json
    const json = response.json();
    
  }
  
  
</script>
<!-- ..... -->

3. Shake that data aka Process it! Shakira, Shakira...🤦‍♀️🤦‍♂️

Like i said before, we just care about a subset of fields for our users:

id, name, username, email, phone

Let's create a preProcessUsers function in charge of stripping down, not needed fields.

To do that we will iterate over the users and return our new array, which should look like this:

[
  {
    "id": 1,
    "name": "Leanne Graham",
    "username": "Bret",
    "email": "Sincere@april.biz",
    "phone": "1-770-736-8031 x56442",

  },
   {
    "id": 2,
    "name": "Charlie Chaplin",
    "username": "Happychap",
    "email": "say_no_to_kaiser_moustache@thegreatdictator.moc",
    "phone": "1-2311-23123-34",

  },
  // and so on...
]

Lets write the function preProcessUsers

<!-- index.html -->
<!-- .... -->
<script>
// ...	

function preProcessUsers(usersArray) {
	// Array.map will loop through the array fields and create a new array
  // based on our logic 
  // more info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
  // an arrow function could also be used
  // usersArray.map(user => {..fn body..});
	const newUsersArray = usersArray.map(function(user) {
    // here we return only the fields we care about for each user
    return {
    	id: user.id, 
      name: user.name, 
      username: user.username, 
      email: user.email
    }
  });
  
  // now we can return our new clean array
  return newUsersArray;
}
  
</script>
<!-- ..... -->

3. Make it pop aka show me some users users 👾🕹

Table Reference: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table

Everybody knows a table has rows and columns.

A table has an header which tells you what fields you are looking at, this is represented by <th>

Our fields id, email , etc, are the columns, <td> elements

Each user will be a table row <tr> Element

<!-- index.html -->
<!-- .... -->
<script>
// ...	
  
function buildTableHead(user) {
  /*
    
    Acording to spec all <th> elements should be inside a <tr>
  	So by definition they are children of <tr>
  	

    ## Tree representation of the DOM nodes for the header ##

              <thead> —> Parent Node or Root inside our context
                |
               <tr> —> Children node of <thead>
               /  \
            <th>  <th> —> Children nodes of <tr>, also called leaf nodes, just like the leafs on a real tree
            
            
  	## Translates to ##
  	
  	<thead>
  		<tr>
  			<th>
  				field 1
  			</th>
  			<th>
  				field 2
  			</th>
  			...
  			th>
  				field N
  			</th>
  		</tr>
  	</thead>
  	
  */
  
  // Counter intuitively we will create this tree in the leafs -> root direction
  // a bottom-up aproach considering the previous graph
  
  // 1. leaf nodes <th>
  // a list of <th> elements
  const thElements = document.createDocumentFragment();
  Object.keys(user).forEach(key => {
 		const thElm = document.createElement('th');
    thElm.textContent = key;
    thElements.appendChild(thElm);
  })
  
  // 2. node <tr>
  const trElement = document.createElement("tr");

  // 3. node <thead>
 	const tableHeadElement = document.createElement("thead");
  
  // 4. Assemble the pieces
  trElement.appendChild(thElements);
 	tableHeadElement.appendChild(trElement);
  
  
  return tableHeadElement;
}
  
  
function buildTableBody(users) {
	const tableBodyElement = document.createElement("tbody");
  
  const trElements = document.createDocumentFragment();
  users.forEach(user => {
		const trElement = buildSingleRow(user);
    trElements.appendChild(trElement);
  });
  
  tableBodyElement.appendChild(trElements);
  
	return tableBodyElement;
}

function buildColumns(user) {
  // aka Column nodes
  const tdElements = document.createDocumentFragment();
	// lets loop over user properties and create the columns
  // value will be the id, name, and so on, e.g. ['1', 'Charlie Chaplin',...]
  Object.values(user).forEach(value =>{
    const tdElm = document.createElement("td");
    tdElm.textContent = value;
    tdElements.appendChild(tdElm);
  });
  
  return tdElements;
}
  
function buildSingleRow(user) {
  const trElement = document.createElement("tr");
  const tdElements = buildColumns(user);
  trElement.appendChild(tdElements);
	
  return trElement;
}
  
function buildAllRows

// Here we assemble our table
function renderTable(tableID, usersData) {
	const tableRoot = document.getElementById(tableID);
  
  // Assemble the pieces
  const tableBodyElement = buildTablebody(usersData);
  const tableHeadElement = buildTableHead(usersData);  
	
  tableRoot.appendChild(tableHeadElement);
  tableRoot.appendChild(tableBodyElement);
}
  
// when page is loaded, meaning all the html, css and so on we start our logic
window.onload = async function() {
	// 1. fetch our data, this is async returns a promise, so we use await keyword
  const usersRawData = await getUsers();

  // 2. preprocess the data
  const processedUsersData = preProcessUsers(usersRawData);
  
  // 3. uff, render the table 😓
  renderTable(processedUsersData);

};
  
// TODO: Add references for the dom apis used here
  
</script>
<!-- .... -->

I don't know about you, but my reason tells me, this is way too much code just to load a simple html table...

What if we need dynamic actions to add, edit and remove users?

b

2. The react way

What if I tell you that 90% of the time you won't need to manipulate the DOM tree?

Yes that's right, no more document.something

I can feel you eyes sparkling already, thinking you have just found the lost treasure of El dorado.

From now on, you will worry about your nice, tidy Components. How to represent your data and how to manipulate it and react will gladly and obidiently manipulate the DOM for you...

1. Every Great Story starts somewhere, I think Walt Disney said that

Let us once again, create an index.html, only this time we will include the react scripts

<!-- index.html -->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
	<div id="reactRoot">
  <!-- This is where react will render our JSX components -->
  </div>
	
  <script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
  <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script> 
  <!--
		babel will take care of the JSX part, it acts as a preprocessor
		this tool gives us a visualization of how JSX look after parsed by babel: https://rajasegar.github.io/ast-builder/
		reference: https://reactjs.org/docs/add-react-to-a-website.html#add-jsx-to-a-project
	-->
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

  <script type="text/babel">
    // Freshly juiced react code goes here 
  </script>
</body>
</html>
<!-- index.html -->
<!-- .... -->

  <script type="text/babel">
  
  class Table extends React.Component {
    constructor(props) {
        super(props);
        // we need to store our users, lets use react state
        // right now its empty, but when the the components mounts in the 
        // dom ComponentDidMount is called by react
        // making the call to the api and getting the data
        this.state = {users: []};
      }

  	// lifecycle method in invoked when component mounts
  	// great to fetch data
  	async ComponentDidMount() {
        const usersRawData = await this.getUsers();
        const processedUsersData = this.preProcessUsers(usersRawData);        
        this.setState({users: processedUsersData});
    }
    
    // ipsis verbis from previous implemntation
    preProcessUsers(usersArray) {

        const newUsersArray = usersArray.map(function(user) {
        // here we return only the fields we care about for each user
        return {
            id: user.id, 
            name: user.name, 
            username: user.username, 
            email: user.email
        }
      });
      
      return newUsersArray;
    }

    async getUsers() {
        const response = await fetch("https://jsonplaceholder.typicode.com/users");
        return response.json();
    }

    renderCols(user, type, element) {
        return Object[type](user).map(colVal => React.createElement(element, null, colVal));
    }

    render() {
      return (<table>
          <thead>
              <tr>
                {this.renderCols(this.state.users[0], "keys", "th")}
              </tr>
              </thead> 
          <tbody>
            {this.state.users.map(user => <tr>{this.renderCols(user, "values", "td")}</tr>)}
          </tbody>
      </table>)
    }
  }
  
  
    // renders Table Component on the designated DOM element
 	ReactDOM.render(
        <Table/>,
        document.getElementById('reactRoot')
   );
  </script>
<!-- .... -->

Such declarative, much expression

TODO:

  • break down the react example and add comments
  • create pages with working example add links
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment