React | NextJS

9.1 React Concepts

Module Still Under Development

# JavaScript Concepts

Two key topics for an understanding of what React is doing for us are state-driven ui and a virtual DOM.

There is nothing that the React framework does that you could not do by yourself with vanillaJS.

We are using a framework because it can help us to more efficiently build a consistent web application.

Now, this does mean that we have to learn the mental model and approach that React uses to solve common problems. The idea is the same as if you were going to build a cabin. You could do it yourself with the tools that you own. However, if you were given a specialized set of tools and parts that were designed by a team of people who had collectively already built 10,000 cabins, then you can imagine how you can start building many more cabins a lot more efficiently.

Before you can be efficient though, you need to learn how to use those specialized tools.

Here is a good article about why people use frameworks instead of Vanilla JS (opens new window)

# State-Driven UI

If you have ever written an app with Flutter and Dart then your probably understand the idea behind having an app interface being built based on a data structure.

STATE IS DATA

When working with frameworks like React, you will see many references to State.

State is just the data attached to the current page and/or component at the current moment.

You can think of the word state as a way to describe the current condition of something.

What is the current state of your:

  • home?
  • car?
  • clothes?
  • credit card statement?

There are probably many words and properties that you could use to describe each of these things. The accumulation of properties and values that you would use as the descriptions is the STATE.

Now imagine that you were going to build a website about your home which had many pages and components displaying information about your home.

Would you rather write a bunch of static pages and repeatedly type the same information about the home, just formatted and organized in different ways? OR, store all the information about the home in a data structure that is shared from one common source across every component.

If you update the information in the common source then any component that displays that information could immediately be updated.

# State in JS

Here is a simple data structure that could represent your home.

const state = {
  rent: {
    frequency: 'monthly',
    cost: 3900.00,
  },
  rooms: [
    {
      name: 'Kitchen',
      occupants: 0,
      isClean: true,
    }
    {
      name: 'Bedroom',
      occupants: 2,
      isClean: false,
    }
  ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

We have a state variable that holds all the information about our home. Because it is an Object, passing it around through functions would pass a reference, not a copy.

# Observing Changes in State

One thing that we cannot do in JavaScript is monitor changes to a variable. If we update the value inside an object, there is no way that anyone knows that a change was made. We need to build this functionality.

Take this code sample as an example.

//create our app state
let state = {
  food: 'cheese',
  drink: 'wine',
};

//display the state in two components
document.querySelector('p.food').textContent = state.food;
document.querySelector('p.beverage').textContent = state.drink;

//listen for a click on some interface button
document.getElementById('btnGetFood').addEventListener('click', (ev) => {
  state.food = 'burger';
  state.drink = 'beer';
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

By the end of this script, if the user has clicked on the button, our state has changed but the textContent of the components is still the original value of state.food and state.drink.

The textContent properties are not "bound" to the state object properties. If a textContent value changes the state object property doesn't have to and vice versa.

So, we need a way to inform everyone when a change happens to state. The way that React addresses this issue, is by having a function that you call any time you need to update state.

//create our app state
let state = {
  food: 'cheese',
  drink: 'wine',
};

function setState(newState) {
  //newState is the object containing properties we want to change in state
  state = { ...state, ...newState };
  //replace ALL of state with its original values PLUS add whatever new props were passed
  // state = {food:'cheese', drink:'wine', food: 'burger'};
  //Note the two food properties. The second overwrites the first.

  //React then internally calls a function like this:
  updateUIComponentsThatUseState();
  //React knows which components are using state
}

setState({ food: 'burger' });
//we have overwritten the value of food inside state AND given
//another function a chance to tell the interface
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

Now, by using this setState function, there is a standard way to call a function that will update the state data being displayed inside any component.

The updateUIComponentsThatUseState function is just a made up name that describes what the function does. It will be using tagged template literals to update / replace content that is attached to state.

//greatly simplified for demonstration purposes
function updateUIComponentsThatUseState() {
  document.body.innerHTML = `<p class="food">${state.food}</p>
                        <p class="beverage">${state.drink}</p>`;
  //just imagine looping through all the components that are using state
  //and replacing them with new elements and content.
}
1
2
3
4
5
6
7

If you are unsure about the rest and spread operators or how destructuring works in JavaScript, here is the MAD9014 reference (opens new window).

# Virtual DOM

One of the reasons that React has become so popular and is used on so many websites, is the way it can efficiently figure out the connection between interface components and the state data and how it knows which components need to be updated.

It achieves this by using a Virtual DOM instead of the actual DOM.

Virtual DOM

A Virtual DOM is simple a JavaScript Object that represents the current content of the DOM.

When you make a change to the actual DOM, you are forcing the browser to re-render your webpage, re-calculate the layout, and re-paint all the content.

If I were to change the contents of an array of 1000 names that was used to render an unordered list on my webpage and then call a function that rebuilt the entire list because there had been a change, then that is a lot of work for the browser to do.

Data Reactivity

The updating of an interface based on changes in state is known as Data Reactivity.

What if I had only changed one name of the 1000 in that array? Rebuilding the whole list is a lot of wasted processing cycles on the computer.

So, what React does is use a simple Object that represents the DOM (the Virtual DOM) and it compares the current contents of the object properties with the new version of the state data. If you only change one name in the state array of names, then React figures out that only one property inside the Virtual DOM has been changed. Then it will update only the one <li> that was changed.

If no change is made to the Virtual DOM, or if the change results in the same content in the Virtual DOM, then React knows that it doesn't have to tell the browser to re-render or re-paint the screen.

DOM Diffing

The search for changes in the Virtual DOM based on changes in state is called DOM Diffing.

We can improve performance even more by creating a Virtual DOM for each component or each state variable. This way, when looking for changes in our DOM Diffing, we only have to look at the specific state object that is being changed.

Note: NOT all State data needs to be held in a global location to be shared across all components. Often there is state data that is specific to a single module. Even though the state object is smaller, the component can still benefit from using the standard React approach to managing state and data reactivity.

So, what could a Virtual DOM look like?

const component = {
  element: 'ul',
  data: state.people,
  properties: [{ id: 'people' }, { title: 'list of people' }],
  children: [
    {
      element: 'li',
      index: 0,
      properties: [{ id: index }],
      children: ['Person One'],
    },
    {
      element: 'li',
      index: 1,
      properties: [{ id: index }],
      children: [
        'Person Two ',
        {
          element: 'button',
          properties: [{ class: 'listbutton' }],
          children: ['Click me'],
        },
      ],
    },
  ],
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

So, our virtual DOM would be rendered into something like:

<ul id="people" title="list of people">
  <li>Person One</li>
  <li>Person Two <button class="listbutton">Click Me</button></li>
</ul>
1
2
3
4

To be clear, this is NOT exactly what React uses. This is just a representation of a generic approach to creating a Virtual DOM that would hold all the needed information without all the overhead of creating actual DOM Nodes.

When doing our DOM diffing, we would be looking specifically at the children properties to see if those were different than the values inside component.data (our state).

# Components

A Component at its most simple is just a chunk of HTML. You can also add styling to a component to give it a consistent appearance. You can also attach JavaScript to the chunk of HTML to give it behaviours.

Components are not required to have state data, but most do.

There is a standard HTML5 Web Component API that we discussed in the hybrid portion of this course. MDN Web Component reference (opens new window). These Web Components are NOT the same as React Components. A React Component is a JavaScript object that will be converted into actual HTML elements by the react-dom package.

We will be using React's own Components which were developed to do much the same things, but long before the Web Component API was available.

If you look at a React-based webpage and are wondering which things are components, then just think of a logical way to group parts of the interface. A navbar, a masthead, a login form, or a list of people. All of those would make sense as a component.

There is no hard and fast rule that says you need to make components as of certain parts of your page. Being able to reuse a component in different screens is usually a good guiding principle. Being able to nest a component inside other components is another.

With very little practice you will be able to design and build lots of your own components.

# React Navigation

When using the React framework alone, what you are building is a Single Page Application (SPA).

React does not have built-in routing between pages. Instead it will just be a single web page running a script on the client-side, in the browser, and it adds, removes, and updates components within that single page.

If you want to build a site that uses multiple web pages, then there are libraries like React Router which adds server-side routing between multiple web pages and each page will have groups of components being controlled by JavaScript (React) in the browser.

A newer, more fully featured approach to routing and building sites with React and multiple web pages is the Next.js framework. Next.js is a website building framework that uses React and, on top of React, adds routing and other features. In a few weeks we will be expanding from just React to React with NextJS.

React vs NextJS

On a conceptual note, when thinking about the difference between React and Next, a default React site will be a Single Page Application, and a default NextJS site will be a multi-page web site.

# Finite State Machines and PubSub

One approach to managing state and informing components about changes in State is with a Finite State Machine and the PubSub (Publish Subscribe) Design Pattern.

The PubSub pattern is an approach that you could use if you were building your own web app but you didn't want to use a framework like React, Vue, or Angular but you still needed to manage state.

The Finite State Machine approach is something that we will look at using when we talk about the React Context API later this semester.

# JS Classes (Not)

Up until a few years ago, the dominant approach to writing React Applications was to create Components using JS Classes.

This course will be using version 18 of React. The recommended approach for components with version 18 is to create them with functions and use the hooks features.

Avoid Classes in React

If you find React samples and tutorials online and they are using class in the code to create components, then you are best to avoid that source. The code will most likely still run, but your code will be out-of-date and written against best practices. Not something that you want to have in your portfolio.

If you want to review how classes work in JS here is the reference about JS Classes in MAD9014 (opens new window)

# Higher Order Functions

In JavaScript, variables can hold any type of Object. This includes functions. This means that you can pass functions to other functions as arguments to be used. It also means that your function can return a function as the result.

In React we are going to be passing functions around between components. Our web pages will be made up of lots of components. These components will be in separate files and sometimes you will need to pass a function from one component, through a series of other functions to get it to the point where you need to use it. This means we will be using Higher Order Functions

A Higher Order Function is a function that accepts one or more functions as arguments and returns a function as a result.

Understanding how JavaScript can pass these functions around without ending up with a whole bunch of copies of the functions is the difference between passing by value or reference.

JavaScript variables all hold either Primitive values or Object references. The Primitive values are String, Number, Boolean, null, undefined, BigInt, and Symbol. If a variable is holding one of these, it is just a value and when passed to a function, it is a copy of the value that is sent to the function.

Everything else in a variable is an Object reference. When passed to a function, it is only a reference to the Object's memory location that is actually passed.

There are actually a couple more Primitive types that may be added to JavaScript soon - Record and Tuple. Watch this video to learn more about those.

# Tagged Template Literals

React uses a format called JSX which is really just template literals with tags (functions) that run an interpret the HTML-like syntax of the strings inside the template literals. The function that runs on the string is called a tag. The combination of the tag function and string literal is called a tagged template literal.

We have used lots of template literals in the first two semesters, so you should be familiar with them.

If you want a refresher about them, here is the MAD9014 reference page about Strings (opens new window).

If you want to refresh what you learned about tagged template literals, here is the MAD9014 reference about tagged template literals (opens new window).

Here is a basic example showing a tagged template literal.

//A basic template literal
let age = 157;
let str = `I am not ${age} years old`;

//A tagged template literal
function validateAge(stringParts, prop1) {
  //stringParts will be an array with each of the string segments around the variables
  //prop1 would be the first variable injected in the template string
  if (isNaN(prop1)) {
    //prop1 is not a number
    return 'I am ageless.';
  } else if (parseInt(prop1) > 130 || parseInt(prop1) < 0) {
    //number is too big or too small
    return 'I have an imaginary age.';
  } else {
    //we are good to go. Return what the template literal would have been
    return stringParts[0] + prop1 + stringParts[1];
  }
}
//our function returns one of three possible values to assign to str.
let str = validateAge`I am not ${age} years old.`;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# References

Remember that you still have access to all the updated course content for MAD9014.

If you are looking for a reference on any standard JavaScript, your best source will be on the Fall MAD014 website (opens new window).

# What to do this week

TODO Things to do before next week.

  • Read all the content from Modules 9.1, 9.2, and 10.1.
  • Create the new Git branch for Phase 3 of your Hybrid Project and start working on that.
Last Updated: 5/5/2024, 9:40:48 PM