React

React

React is a JavaScript library for building user interfaces. It is a front-end JavaScript library.

React is used for single-page applications. It breaks down the User Interface structure to a component tree. The individual components are re-rendered when changes occur.

  • Go to CodeSandBox for working in a browser-based codespace.
  • For local development, follow these steps:
    • Install node
    • Create a vite application
        npm create vite@latest my-react-app --template react
      
    • Select React as framework and JavaScript as the variant.
    • Enter the directory and install dependencies
        cd my-react-app
        npm install
      
    • Start the deployment server.
        npm run dev
      

      There is one other way, which is to use npm create-react-app myApp, which might take longer.

    • Open the app on the browser by heading over to http://localhost:5173/

To require the dependencies,

var React = require("react");
var ReactDOM = require("react-dom");

The new way is to do import / export directly (ES6 feature).

import React from "react";
import ReactDOM from "react-dom";

JSX and Babel

React works by these .jsx files. These files have HTML in a JavaScript file. The HTML is picked up by a compiler and rendered.

Babel is a JavaScript compiler. It compiles next-gen JavaScript like ES6 to browser-compatible version. It is present in the React module we have imported above.

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>JSX</title>
    <link rel="stylesheet" href="styles.css" />
  </head>

  <body>
    <div id="root"></div>
    <script src="../src/index.js" type="text/JSX"></script>
  </body>
</html>
import React from "react";
import ReactDOM from "react-dom";

ReactDOM.render(<h1>Hello, world!</h1>, document.getElementById("root"));

The ReactDOM.render() method was marked ‘deprecated’ in React 18 and the new way is to use ReactDOM.createRoot().render()

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <h1>Hello, world!</h1>
);

The render method takes a single HTML element and displays it inside another element.

JSX is a Syntax Extension of JavaScript. In React, we combine elements, styles and behaviour together.

To insert JavaScript expression inside the html, use {}

const name = "Neo";
ReactDOM.render(<h1>Hello, {name}!</h1>, document.getElementById("root"));

ReactDOM.render(<p>Hello, {34 + 82}!</p>, document.getElementById("root"));

ReactDOM.render(<p>Hello, {`Mr. ${name}!`}!</p>, document.getElementById("root"));

We cannnot insert JavaScript statements - loops, conditionals etc. like above.

Attributes in JSX must be in camelCase.

ReactDOM.render(<p className="heading">Hey</p>, document.getElementById("root"));

Best way to style the elements is to add classes to elements using className and describe the styles for the classes in a CSS file.

To use inline styles, we need to represent the values as a JavaScript object. The css properties must be converted from kebab-case to camelCase (font-size to fontSize).

ReactDOM.render(<p style=>Hey</p>, document.getElementById("root"));

Style Guide for React

React Components

Start with a function with name in PascalCase

function Heading() {
  return <h1>Good Morning</h1>;
}

This is a component. We can use it while rendering the page by

ReactDOM.render(
  <div>
    <Heading />
    ...

The componets should be separated into individual files with the extension .jsx and leave the index.js just as it is.

Inside Heading.jsx, do

import React from "react";

function Heading() {
  return <h1>Good Morning</h1>;
}

export default Heading;

To use a component, we have to just import it.

import Heading from "./Heading";

Usually, we only render one component: App in the index.js file, and in a component named App.jsx, we include all the import statements and the rendering logic.

Props

Props are arguments passed to React components. They are passed via HTML attributes.

We can use props by passing them to a component. They are treated as JavaScript objects.

function Card(props) {
  return (
    <img src={props.img} alt="alt">
    <h1>{props.name}</h1>
    <h3>{props.id}</h3>
  );
}

We can use them just like using HTML attributes.

  ...
  <Card 
    img="https://www.picsum.com/200"
    name="Neo"
    id="1562323"
  />

Map

Map is used to generate multiple components. We apply the map method to a list of JavaScript objects, and it takes a function as an argument. The function is applied to each object in the list of objects.

listOfObjects.map(myFunction);

We can use map to create repeating elements.

function createCard(contact) {
  return (
    <Card
      key={contact.id}
      name={contact.name}
      img={contact.imgURL}
      tel={contact.phone}
      email={contact.email}
    />
  );
}
function App() {
  return (
    <div>
      <h1 className="heading">My Contacts</h1>
      {contacts.map(createCard)}
    </div>
  );
}

React creates a virtual DOM to represent the components. To create this, Each component rendered using a method like map should have a unique field named key.

Filter & Reduce

The Filter method filters an array and only keeps the elements that meet a particular criteria (predicate).

The Reduce method accumulates a value by applying a function to each item in an array.

// Map: Double each element
let newNums = nums.map(function (x) {
  return x * 2;
});
// Filter: Only keep numbers that are < 10
let newNums = nums.filter(function (x) {
  return x < 10;
});
// Reduce: Sum an array
let newNums = nums.reduce(function (accumulator, curr) {
  accumulator += curr;
});

Find & FindIndex

These are functions introduced newly, which lets us find the first item that satisfies a condition, and find the index of the first item satisfying the condition, respectively.

// Find
let fistNoGT10 = nums.find(function (x) {
  return x > 10;
})
// Find Index
let indexOfFirstEven = nums.findIndex(function (x) {
  return x % 2 == 0;
});

Arrow Functions

Arrow functions are a concise way of representing functions. They utilize the ‘fat arrow’ notation: =>. We can use them along with map and filter to render repeated components.

// Suppose we have a list 'notes' and want to render the 'Note' component for each note
  ...
  <Header />
  {notes.map((note) => (
    <Note 
      key={note.key}
      title={note.title}
      content={note.content}
      />
  ))}
  ...

Conditional Rendering

Conditional rendering is done using the ternary operator

condition ? if_true : if_false

We cannot include JavaScript statements inside components rendered, using {} since they only support expressions. But since ternary operators are expressions, we can use them.

let isLoggedIn = false;

function App() {
  return (
    <div className="container">
      {isLoggedIn ? <h1>Hello</h1> : <Login />}
    </div>
  );
}

To show something only when a condition is satisfied, we can use null, or the ‘and’ operator: &&

{isLoggedIn ? <h1>Hello</h1> : null}
// OR
{isLoggedIn && <h1>Hello</h1>} 
// Condition && Expression

&& works as the Guard Operator, preventing the expression evaluation unless the condition is true.

State & Hooks

UI = f(State)

A state is a JavaScript object that stores data and can change over time and affect how a component renders. State is local to the component that defines it, and changes to the state can only be made by that component.

Hooks are functions that allow us to manipulate the state of a component. The most commonly used hook is useState. Hooks can only be used inside a function’s body.

const state = React.useState(0);

The useState() method returns an array with the argument as the initial value (initial state), along with a function. We can use Destructuring to access the value and function separately.

const [count, setCount] = React.useState(0);

Now, the count variable stores the state, 0, and the setCount can be used to update state.

function increment() {
  setCount(count + 1);
}

We can use React to make the components responsive, by integrating with HTML events like onClick, onMouseOver, onMouseOut etc. (HTML Events Reference)

let normalStyle = { "background-color": "white" };
let hoverStyle = { "background-color": "black" };

function hoverEffect() {
  setButtonStyle(hoverStyle);
}
function removeHoverEffect() {
  setButtonStyle(normalStyle);
}
return (
  <button
    onMouseOver={hoverEffect}
    onMouseOut={removeHoverEffect}
    style={buttonStyle}
  >
  ...

While handling events, we can obtain the event itself as an attribute, just like vanilla JS.

// Using event 'onChange' on an input field
function handleChange(event) {
  setName(event.target.value);
}

We can use a single useState() to control multiple states using an object.

const [fullName, setFullName] = useState({
  fname: "Akshay",
  lname: "Rajan",
});
// fullName is an object with value {fname: "Akshay",lname: "Rajan"}

We can update the state conditionally by accessing the previous state of the component like this;

setFullName(prevValue => { // prevValue is the previous state
  if (name === "fName") {
    return {
      fName: value,
      lName: prevValue.lName
    };
  } else if (name === "lName") {
    return {
      fName: prevValue.fName,
      lname: value
    };
  }
});
  • The ES6 Spread operator ... can spread or expand an or object.

    let arr1 = ["apple", "banana"];
    let arr2 = ["pineapple",...arr1, "mango"];
    // arr2 becomes ["pineapple", "apple", "banana", "mango"]
    
    let name = {fname: "John", lname: "Doe"};
    let user = {    // 'user' now contains the 2 attributes
      ...name,      // 'fname' and 'lname' in the 'name' object
      id: 1,
    };
    

We can use the spread operator in React code to avoid repetition.

function handleChange(event) {
  const { name, value } = event.target;

  setContact((prevValue) => {
    return { 
      ...prevValue, // Keeps the previous values
      [name]: value, // The 'name' must be inserted as an array
      // Otherwise it will be interpreted as a string
    };
  });
}

We can pass in the index of an array as the key, while using map, like

{items.map((data, index) => <ToDoItem key={index} data={data} />)}

We are able to not only pass data, but also functions as props. This allows us to maipulate components like deleting an Item from a List.

Material UI

A library of pre-built React components based on the Google, Material design.

npm install @mui/material @emotion/react @emotion/styled

useEffect

useEffect() hook is triggered every time the render method is called. We use this to trigger something whenever the components are re-rendered.

This hook takes 2 arguments: A function to execute on re-rendering, and a condition to check before re-rendering.

useEffect(() => {
  fetchData();
}, []); // Empty array is used to avoid re-rendering infinitely