ReactJS project based training with Road Trip Planner App

In-depth introduction to ReactJS for beginners

From basics down to the nitty-gritty of ReactJS:

This article is divided into three -

  • This article talks about basic React Concepts crucial to start the what, whys, and how of ReactJS.
  • Then we’ll set up the project directory and environment
  • next one teaches basic react syntax to code reusable components using a hands-on project

What is React

React is a javascript library that lets you build a website with small isolated pieces called React components, like building legos.

Let's now break down the official definition of React

React is a declarative, efficient, and flexible JavaScript library for building user interfaces.

declarative: We use components to tell React what a particular part of UI should like on the screen. How it happens isn't our concern, react handles that for us.

efficient: You’ll see how React applications are so much more efficient than the ones created with vanilla javascript, detailed discussion in VDOM section.

flexible: React helps you build re-usable components, you can use react components inside traditional code without having to re-write the whole project, besides it is said to be flexible as you can use it for not only web but also to create mobile applications.

How it works

React basically handles the view layer of the application by following a Component-driven-development (CDD) which is the core idea of React application development.

It breaks the UI into a component hierarchy.

Composition and inheritance

See on Twitter for different sections and different components even can bring down to basic /atomic components for the profile picture or username

Features of React

JSX

 //syntax-1 to write React element 
const Heading = <h1>Create a new Trip</h1>
You see that in the above code a javascript variable is assigned an HTML element, now if you just learned basic HTML and javascript, you might think how come Javascript and HTML are on the same line of code? It isn’t HTML, it is JSX - Javascript, and XML, JSX is an extension to javascript,
  • which makes it easier to write UI logic inside Javascript,
  • and it also allows React to show useful error messages and warnings related to UI

Without JSX you would have to use a method React.createElement() and pass the typical HTML element into it as a string, for every element you want to create. For eg the above code is equivalent to

//syntax-2 to React element
const Heading = React.createElement(
  'h1',                    //type of HTML element
  {},                      //props - will be discussed
  'Create a new Trip'      //children can be anything like an HTML element or another React component or a String like in this case
)

React doesn’t want you to write JSX, it’s your choice to use any of the above methods but note that inherently JSX gets converted into calls to React.createElement(), which means ultimately syntax-1 gets converted into syntax-2

  • Embedding Javascript Expressions in JSX

    Many times you’ll want to use javascript inside JSX, like in this case - you’ve got a React element inside a JS variable called Heading, now suppose you want to render it inside a div,

    • you can do it curly braces { } (we’ll see more use cases in the project tutorial)
<div className="planning-board-header">
	{Heading} 
</div>

//is Equivalent to 

<div className="planning-board-header">
	<h1>Create a new Trip</h1> 
</div>

Virtual DOM

  • DOM

    You might know what DOM is, and if not, in simple terms the HTML doc structure (the webpage structure) is represented as DOM (document object model). It is a tree structure that represents the relationship between different HTML nodes or elements. It’s not a part of the javascript, but a part of the browser, and every browser has DOM APIs to help different programming languages interact with the DOM.

    So to make modifications to the web page, Javascript understands and interacts with this object known as DOM and does so by querying and updating it, using DOM APIs like document document.getElementById().

    The point is that after every single update made by JavaScript, DOM re-renders the entire webpage (which means it re-paints everything on the screen) on every little update. In this process, all querying and updating are light and fast javascript operations and thus cheap. But can you imagine the cost of a browser re-writing the entire page which involves re-calculating CSS and re-running javascript for each element whether it’s updated or not? It’s not just costly but also sounds highly inefficient.

  • VDOM

    This is where Virtual DOM comes into the picture. In short, the Virtual DOM will help you re-render only the elements that are updated.

    render: means to insert dom nodes inside the actual dom tree

    Virtual DOM is just a plain javascript object representing the actual DOM. All updates are made to this VDOM rather than the actual DOM, updating VDOM is cheaper because it doesn't write to the screen.

    How VDOM works?

    • On render, a virtual representation of the UI is stored as a tree-like data structure, using a javascript object, in the memory - which is called VDOM or a virtual DOM tree.
    • On any further update no matter how small, a new VDOM tree is generated, because it’s really cheap - just a plain javascript object remember?
    • Starting from the root node both of these VDOM trees are compared using the diffing algorithm, to see what elements have changed.
    • After the changes are concluded, a React library called ReactDOM ensures that actual DOM updates and re-renders only the changed elements. This process is called reconciliation.

VDOM DOM diagrams goes here

Fundamentals

Components

The isolated code chunks that a React UI is made up of, are called components. They are like reusable javascript functions, optionally accepting some input and returning the content (in form of React elements) to be rendered on the webpage.

To create a UI using this approach, we split the UI into independent units (components), once we have identified these units, we arrange them into a Component hierarchy. Components that appear within another component are called child components in the hierarchy, and this is how we identify the parent-child relationships among components (later we’ll see how useful it is to know these theoretical facts to put it all together).

Let’s see how to define components in React, think of it molding a stamp once and then using it any number of times to create the instances.

There are two ways in react you can define components:

Functional Component

Functional components are the easiest way to build a React component. They are just ES6 javascript functions,

  • following Pascal naming convention,
  • should always return a JSX element,
  • accepts input as something called props, more on that later

Let’s create a location component for our app,

function Location(props){
  return <div> Jaipur, India </div>;
}

Class Component

To create a class component we create an ES6 class extending React.Component by which JS understands that this is a component and not just a plain ES6 class. Class components are a little more complex to build than functional components with some extra features available to them. Class components should have a render method that returns a JSX element or null.

class Location extends React.Component{
constructor(props){}  //optional for now
render(){
  return <div> Jaipur, India </div>;
}
}

Creating and using component Instances

To make use of these components you have to create instances of them. For example, you might declare a Location component using a class or function once and for all, but you will have a number of Location instances on your running app, each showing a different location.

class Planning extends React.Component{
  return (
    <div>
      <Location/>
			<Location/>
			<Location/>
			<Location/>
    </div>
  );
}

Component Hierarchy Check: The planning component contains instances of the Location component, we can say Location is a child component of Planning.

Now, you might have noticed that all of the four Location instances inside Planning are going to show the same location, yes! because we hard-coded it while declaring the Location component.

This was to give you an idea of what the most basic React components look like. But you see, just like Location, in most cases, we have to render some sort of dynamic data on the screen using these components. Which means components have to hold and handle this data somehow.

Props and State

There are two ways to manage data in React components, which can directly impact the output of the component

  • To pass data from the parent to children component we use props
  • To manage data within a component we use state variables

Props

Attributes passed to JSX elements or component instances are called props, props is short for properties, and it is received as function parameters by a functional component, and as constructor parameters by a class component.

It is a javascript object that can be used directly using a dot operator or can also be destructured.

//using props with a dot operator
function Location(props) {
  return <div>{props.location_name}</div>;
}

//using props by destructuring
function Location({ location_name }) {
  return <div>{location_name}</div>;
}

class Planning extends React.Component {
  locations = ["Jaipur, Rajasthan", "Ajmer, Rajasthan", "Jodhpur, Rajasthan"];

  render() {
    return (
      <div>
        <Location location_name={this.locations[0]} />
        <Location location_name={this.locations[1]} />
        <Location location_name={this.locations[2]} />
        <Location location_name="Udaipur, Rajasthan" /> 
      </div>
    );
  }
}

Note that you can only pass props from parent to children and not otherwise, it is called unidirectional dataflow in react, we’ll explore this concept later.

State

These are the in-built javascript objects, they are used as variables declared within a function or class, but the catch here is that any change in state variable will trigger a re-render of the component, also you should always change the state variable using a specifically given method called setState.

Note that if you change a state directly by re-assigning, it won’t cause an error, but it will not trigger the re-render, which is the main purpose of using a state variable.

This might all sound confusing, so imagine in the above example user was entering the locations one by one and expecting them to show up on the page, which means whenever the user enters a new location, the whole Planning component would be re-rendered with a newly added location.

Let’s convert the locations into a state variable, for simplicity we’ll keep just one dynamic location.

function Location(props) {
  return <div>{props.location_name}</div>;
}
class Planning extends React.Component {
    constructor(props) { 
        super(props);
        this.state = { location: "Jaipur, Rajasthan"} // we assign have to assign the initial value here
    }
// this function has to be called in order to change the state variable and re-render the planning component with new location
// it will be used by user input onChange event handler
changeLocation(){
		this.setState({location: userInput})
}
  render() {
    return (
      <div>
        <Location location_name={this.state.location} />
      </div>
    );
  }
}

Inside a functional component, writing states looks different, a hook called useState is used to create and update state variable. So instead having all of your state variables inside this.state object, you’ll have a useState hook for each state variable. Like this,

const [input, setInput] = useState("");
const [inputDate, setInputDate] = useState("");
const [inputTime, setInputTime] = useState("");

How it works ?

useState accepts the initial value of the state, and returns an array of two things - the current state and the function that is used to update the current state. So here, we have one state updation method for every state variable which rewrites the state variable every time it gets updated, unlike class components, where this.stateUpdate merges the previous state with the new one.

//FUNCTION COMPONENT 
const [input, setInput] = useState("");
const [inputDate, setInputDate] = useState("");
const [inputTime, setInputTime] = useState("");
//state updation in functional component 
setInput("new location name");
setInputDate("new date");

//now the first two input state contains the new data while inputTime remains empty, it's not rewritten

//CLASS COMPONENT
this.state = {
  input: "",
  inputDate: "",
  inputTime:""
}; 

this.setState(
  {
    input: "new location name",
    inputDate:"new date",
  }
)

//merging the old and new state, the current state variable will look like 
{
  input: "new location name",
  inputDate: "new date",
  inputTime: ""
}

Rendering Elements or Components

You defined components, and created instances, now you have to actually render them or insert them inside DOM. So how will you paint this component on the webpage - for that you create a div inside your HTML file typically named “root”,

 <div id=”root”>Every thing that is React, goes inside this node</div>

This “root” DOM node act as an entry point for React to access the actual DOM, everything inside this node will be handled by React. To give attach React to this DOM node,

You create a root object in React using ReactDOM.createRoot() by passing the reference to the “root” DOM node inside it. Once you have a root object, you render the react element or component by passing it inside the root.render()

Root render structure

const root = ReactDOM.createRoot(
  document.getElementById('root')
);
root.render(React.createElement(Planning)); 
// or
root.render(<Planning />); 

Component Life Cycle

Each component in React has three phases, you might need to perform some extra tasks other than just rendering, inside the component based on the phase that it is in, let’s perform this task using a function called extraTaskFunction() . For each phase, some Lifecycle methods are used for class components and Hooks are used for functional components.

Mounting

Mounting happens when a component is created and inserted into DOM.

For example, you might want to make an API call and request some data which is to be used by this component, so you would want to make this API call only after the component is mounted into the DOM.

  • for class components ComponentDidMount() is the lifecycle method, inside this method, you’ll make that API call
  • for functional component useEffect(extraTaskFunction, [ ]) Hook is used with an empty array, so this extraTaskFunction will run once the component is mounted into the DOM, and also after every render or re-render.

Updating

This is the longest phase of this lifecycle, as the name suggests after the component gets mounted, it stays in updating phase. Methods related to this phase are called when an update event occurs like changes in props and state variable

For example, suppose you want to make an API call based on the user input, which is stored in a state variable name input and now you want to run this task/API call, whenever this input variable changes

  • for class components ComponentDidUpdate() is the lifecycle method, inside this method you’ll make that API call
  • for functional component useEffect(extraTaskFunction, [ input ]) Hook is used and the state or prop variable is passed inside the array in the second argument, so instead o running after every render, extraTaskFunction runs only whenever the variable input changes,

Note that render() and some other methods are also part of the Mounting and Updating phases but I have only used the most commonly used to make my point.

Unmounting

This is when a component is removed from the DOM

This method is used to perform necessary cleanups, for example, all the API calls you’ve been making during after mounting and updating phases, you need to properly unsubscribe to them to prevent memory leakage

  • for class components componentWillUnmount() is the lifecycle method, inside this method you’ll perform unsubscribing, as it runs just before unmounting the component
  • for functional components, extraTaskFunction() inside Hook should return a function performing subscription

Component Interaction

Now that you have a fair understanding of components in React, let’s get back to React Features, which explain how components interact with each other and how component logic and component view interact with each other.

Unidirectional data flow

When React components are in a parent-child arrangement, data flows from parent to children component and never back. In other words, only higher component can pass data to lower components using props, in the component hierarchy. Like this data flows throughout the application in a single direction, it is called unidirectional data flow in React and it provides a better control over the entire application.

So far we talked about how a parent component talks to a child component using props. But there is a possible way to communicate back to parent from child component - using functional props. The functions passed to child component as props might have a setState() inside it, which belongs to the parent component, so child component can use this function prop and alter the parent state.

Unidirectional data flow in react.js

Note: We’ll see a practical use case of this in our project blog.

One-way data binding

Whenever the state (data) changes, components or elements consuming that state will also change or re-render, but not the other way around. It simply means that if any change occurs in the React element associated with a state, it won’t reflect in the state and the state remains unchanged. It is called one-way data binding.

This might sound superficial to you right now, so let’s understand with a quick example, altho you’ll find this use case in the project directly, - suppose you have an input element inside your component and a myinput state variable associated with its value attribute.

const [myinput, setMyinput ]= useState('') // useState hook for state variable inside functional component
const [input, setInput ]= useState('')

<input
    type="text"
    name="destination"
    className="input-field"
    placeholder="City, State"
    value={myinput}      
  />

<input
    type="text"
    name="destination"
    className="input-field"
    placeholder="City, State"
    value={input}
    onChange={(e) => setInput(e.target.value)}         
  />

If you have worked with input elements before, you’d know that when this input element is rendered on screen and you start typing inside it, its value should change accordingly, but you’ll find you cannot see what you’re typing in the input box. This is because value of first input element is set to myinput and it’s initialised with an empty string, and note if there was two-way data binding, change in the value by user will also change myinput.

As we have one-way data binding in React, we have to make it work, so in the second input element, we use an event handler called onChange to update the state variable with whatever value our target/input holds whenever a user types something.

I hope it makes sense, and even if it’s not much clear here, it’s okay to just get the idea of it and it’ll be more clear when you do the hands on practice of putting all the pieces together.