Coding Bootcamp Week 8: DOM, React – Components, State & Hooks

Frontend Development

Reem Dalvi
7 min readMar 5, 2023
Photo by Lautaro Andreani on Unsplash

Document Object Model | DOM

DOM is a programming interface for web documents that allows programs to dynamically access and manipulate the content, structure, and style of web pages.

The DOM is an object-oriented representation of the web page, which can be modified with a scripting language such as JavaScript.

It represents the web page as a hierarchical tree-like 🌲 structure, with each element of the page (such as paragraphs, headings, images, and links) represented by a node in the tree.

// changing element content
const paragraph = document.getElementById("my-paragraph");
paragraph.innerHTML = "New text";

// Adding and removing elements
const div = document.getElementById("my-div");
const button = document.createElement("button");
button.innerHTML = "Click me";
button.addEventListener("click", function() {
alert("Button clicked!");
});
div.appendChild(button);

HTML DOM events

React

Introduction

“A JavaScript library for building user interfaces” — React

Manipulating the DOM can be slow and complex due to its tree structure, requiring recursive traversal. React provides a virtual DOM, allowing us to create User Interface (UI) declaratively with JavaScript without using browser DOM methods.

The virtual DOM provided by React is faster and more efficient than the traditional DOM since it checks for updates based on the declared UI, only updating what is necessary. This enables React to easily “hydrate” a page with data from API requests on the client side.

JSX is a syntax extension of JavaScript that allows us to write HTML syntax inside their JavaScript files. Babel is a JavaScript compiler that converts modern ES6 JavaScript syntax into browser-compatible JavaScript, and can also automatically import jsx-runtime, making JSX code look like normal JavaScript values.

import ReactDOM from 'react-dom';

const element = <h1>Hello, world</h1>;

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(element);

React 17 and later versions automatically import jsx-runtime, while previous versions required the import of React in order to use JSX.

JavaScript can be embedded within JSX using curly braces.

const person = {
name: 'Mary',
age: 24,
};

return (
<section>
<h1>{person.name}</h1>
<p>{person.age} years old</p>
</section>
);

Components

React components are independent and reusable units that allow us to split our apps into smaller, more manageable pieces.

Components can be either function or class-based, but before the introduction of React hooks, class-based components were the primary way to manage state within React.

However, functional components can now handle state using hooks, making them a more versatile option for developers.

React components can be rendered using <> brackets, similar to HTML elements.

// App.js
import { Header } from './Header'

const App = () => {
return (
<div>
<Header />
</div>
);
};

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(<App />);

// Header.jsx

export const Header = () => {
return <h1>App Title</h1>;
};

Components can also render child components, forming a parent-child relationship. To pass information between components, React provides a mechanism called props, which are key-value pairs similar to HTML attributes. Props are passed from the parent component to the child component, but not vice versa.

const App = () => {
return (
<div>
<Book title={'To Kill a Mockingbird'} author={'Harper Lee'} />
</div>
);
};

const Book = (props) => {
return (
<div>
<h2>{props.title}</h2>
<p>{props.author}</p>
</div>
);
};

As the complexity of an app increases, we can isolate and work on individual components to deal with one piece of functionality at a time.

State

React state is used to keep track of any data in applications that may need to change over time, making the app dynamic. By using state, we can update the information rendered on a page, which otherwise would remain static.

React’s useState hook allows us to create a state variable that can be updated over time, making it more dynamic by tracking changes over time. This hook takes one argument, the initial value of the state, and returns an array with two elements: the current state value and a function to update the state value, which can be accessed using destructuring assignment.

import { useState } from 'react';

const Counter = () => {
const [count, setCount] = useState(0);

const handleIncrement = () => {
setCount(count + 1);
};

const handleReset = () => {
setCount(0);
};

return (
<div>
<h1>Counter</h1>
<p>{count}</p>
<button onClick={handleIncrement}>Increment</button>
<button onClick={handleReset}>Reset</button>
</div>
);
};

In this example, the Counter component initialises a state variable called count with an initial value of 0. Two functions, handleIncrement and handleReset, update the count state variable when called by incrementing or resetting it, respectively.

The component renders the current value of count and two buttons, ‘Increment’ and ‘Reset’ that trigger the corresponding state update functions when clicked. As count changes, React automatically updates the rendered HTML to reflect the new value.

The setCounter functions can be called in two ways: directly passing the new value of the state, or passing a function that returns the new state value.

When the new state value depends on the current value, it's recommended to use the function approach to ensure that the value is correct even when the state is updated multiple times quickly.

In more complex cases, it's recommended to extract the state updates into helper functions. The example below shows a counter component that increments and decrements a count value.

const App = () => {
const [count, setCount] = useState(0);

const incrementCount = (increment) => {
setCount((currCount) => {
return currCount + increment;
});
};

return (
<div>
<h1>Count : {count}</h1>
<button onClick={() => incrementCount(1)}>Increase Count</button>
<button onClick={() => incrementCount(-1)}>Decrease Count</button>
</div>
);
};

React state is designed to be replaced, not mutated. We should use the setState functions to replace old values instead of directly mutating them.

We can pass any value from a parent component to a child component via props in React. This allows us to extract functionality into separate components.

The child components can update their parent’s state through the use of the setState functions, which can be passed as a prop. We should keep in mind that the rules of state still apply, and we should never mutate state directly.

A good rule of thumb is to place your state as low as possible, but as high as necessary.

When organizing data in React state, it’s important to consider factors such as avoiding duplicate data, deriving necessary information, keeping data flat, use cases in your application, extensibility, and performance.

The state should be a complete representation of the UI, but unnecessary information should be avoided. It’s also possible to derive data from the state instead of adding additional state variables.

This can be done by creating functions that extract the necessary information from the state. Testing these functions can help ensure their accuracy.

Controlled components

Controlled components are used to keep the state as the source of truth and to be in control of what value the input has.

A component is controlled if its value is set with a prop and changes to that value are handled by React.

Uncontrolled components keep their own internal state and can be useful for integrating React into non-React projects.

A React onChange event is triggered by any change to the inputs value and inside the onChange function, the event.target.value is used to read the incoming change and update the React state accordingly.

import React, { useState } from 'react';

const NameForm = () => {
const [name, setName] = useState('');

const handleSubmit = (event) => {
event.preventDefault();
console.log('Name submitted: ', name);
};

return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input
type="text"
value={name}
onChange={(event) => setName(event.target.value)}
/>
</label>
<button type="submit">Submit</button>
</form>
);
};

export default NameForm;

In this example, we have a NameForm component that renders a form with a single input field for the user to enter their name.

The input value is stored in the name state variable, which is updated whenever the user types in the input field using the onChange handler.

When the form is submitted, the handleSubmit function is called, which prevents the default form submission behavior and logs the entered name to the console.

Emotional check ⏩

Transitioning from backend to frontend in a rapid manner has proven to be quite challenging, given more time, I would have preferred to solidify my understanding of backend concepts more thoroughly.

However, the nature of bootcamps requires a swift pace in order to cover a wide range of topics. The accelerated learning pace has been valuable too and I have absorbed a great deal of information in a short period of time.

The bootcamp offers a multitude of hands-on mini-projects, which provide ample opportunities to gain practical experience. As long as I grasp the underlying concepts, I believe that continued practice will eventually result in greater confidence and proficiency in implementation.

Leap through time

--

--

Reem Dalvi

I like finding parallels between biological and computer science