The benefit of React is that it only updates DOM elements that have changed by managing a virtual DOM. It also allow us to swap component out of webpages.

Primary Libraries :

  • import React from 'react' : for pure React purposes; the library does not interact with the DOM; mainly use for creating components or writing JSX elements
  • import ReactDOM from 'react-dom/client' : contains methods for React to interact with DOM

JSX

JSX is the language use for React. It's a combination of Javascript + HTML. JSX is used everywhere between the opening and closing tags of JSX elements

JSX Elements

  • JSX Elements are the same as HTML Elements, like div button input p except they're in Javascript file, but they are not the same as React components
  • They can contain attributes like id
    • The HTML attribute class can not be used in JSX, instead classNamehas to be used instead because class is a reserved Javascript keyword
  • They can be assigned to variables and pass around like functions
  • They must be wrapped in parenthesis () if the element require multiple lines of code
  • There can only be one outermost html element in a jsx element

Differences in React :

  • The HTML attribute class can not be used in JSX, instead className has to be used instead because class is a reserved Javascript keyword
  • Elements that have a self-closing tag MUST have a forward slash before the final bracket
    • Example: <img src=""/> <input name=""/> <br/>
  • JSX elements must be wrapped in parenthesis () if the element require multiple lines of code
  • Similar to injecting Javascript ${expression} in string, it's possible to inject Javascript into JSX expressions too, but using only curly braces {js expression}
    • Example: <img src={variableName}/> : there is no need for quotation mark
const jsxElement = (
    <div className="top-bar">
        <div className="top-nav">
            <button id="menuBtn" onClick={functionName}>Menu</button>
            <a href="#">Link</a>
        </div>
    </div>
);

Rendering JSX Elements

  • JSX element typically render to one react root.
  • Use createRoot() method to create the root from 'react-dom/client' library
  • Use render() method to render the JSX element
import React from 'react';
import ReactDOM from 'react-dom/client';

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

// HTML
<div id="root"></div>

Conditional Rendering

  • You can use the ternary operator and && to make conditional rendering, but notif statements
// Using ternary operator
const headline = (
  <h1>
    { age >= drinkingAge ? 'Buy Drink' : 'Do Teen Stuff' }
  </h1>
);

// Using && operator
const tasty = (
  <ul>
    <li>Applesauce</li>
    { !baby && <li>Pizza</li> }
  </ul>
);

Using array.map() Method

The map() method is very useful for building repetitive elements from an array.

// Mapping a string array
const strings = ['Home', 'Shop', 'About Me'];
const listItems = strings.map(string => <li key="string">{string}</li>);
root.render(<ul>{listItems}</ul>);

// Output
<ul>
  <li key="Home">Home</li>
  <li key="Shop">Shop</li>
  <li key="About Me">About Me</li>
</ul>

// Mapping a set
const navSet = {
  'Home': '/index.html',
  'Shop': '/shop.html',
  'About Me': '/aboutMe.html'
};
const navMenu = Object.keys(navSet).map(([name, url]) => <a key={name} href={url}>{name}</a>);
root.render(navMenu);

// Output:
<a key="Home" href="/index.html">Home</a>,
<a key="Shop" href="/shop.html">Shop</a>,
<a key="About Me" href="/aboutMe.html">About Me</a>

JSX key Attribute

When rendering with map(), React uses the key attribute to identify which element was mapped from which value. This allows React to update/remove only the necessary element when the list changes.

  • key must be unique among siblings element, but they don't have to be globally unique
  • Don't use the array index as a key unless you're sure items never reorder or get removed.
    • Combining value and index to make a key can work, but list can not be re-ordered:key={array[i] + i}
  • Keys are not accessible inside the component via props
  • ALWAYS try to have the key attribute for repetitive elements as it makes rendering more efficient

React Component

  • Component: for React, it is a block of code that renders HTML and re-renders whenever some data changes
  • Functional Component: a JavaScript function that returns a JSX element (a component)
    • Functional components MUST begin with a capital letter, or the React compiler will not recognize it as a React component
      • The JS file of the component should also have the first letter capitalized for easy recognition.
    • They can be used like JSX elements: nesting other components and adding attributes, but require passing and using props within the function
    • // MySimpleComponent cannot accept attributes or have nested children
      function MySimpleComponent() {
        return <div>Hello, I'm a functional React Component!</div>;
      }   
      
      // MyNewComponent can accept attributes and nested children because it uses props
      function MyNewComponent(props) {
        return (
          <div className={props.className}> {props.children || "Hello, I'm a functional React Component!"}</div>
        );
      }   
      
      // Using functional component
      const nestedComponent = (
        <MyNewComponent className="top-bar">
          <OtherComponent />
        </MyNewComponent>  
      );
      
      // Rendering functional component
      root.render(<MySimpleComponent />)

Functional Component (with Hooks) Lifecycle

Sequence: Mount → Render → Update (re-render on state/props change) → Unmount

  1. Mounting:
    • Runs the function once
    • useEffect(() => {... }, []) runs after the initial render
  2. Updating:
    • Function runs again when state or props change
    • useEffect(() => {... }) runs after every render by default
    • useEffect(() => {... }, [deps]) runs when dependencies change
  3. Unmounting:
    • Cleanup function inside useEffect runs

Props

props are used to pass information from one component to another (like className, style, children nodes)

  • The functional component adds props into its argument to indicate it will accept attributes
  • By giving attributes to the component, the attributes and their values are passed to the functional component via props
  • Add curly braces to pass JavaScript expressions like functions, arrays, or objects
  • Use useContext if you're passing props deeply or need shared state across multiple components
// Passing info to Button component as attributes
root.render( 
  <Button className="submitBtn" onClick={myFunction} data={[1, 2, 3]} /> 
);

// The functional component accesses the info using props
function Button(props) {
  calcData(props.data);
  return <button className={props.className} onClick={props.onClick} />;
}

// Using destructuring to simplify
function Button({ data, className, onClick }) {
  calcData(data);
  return <button className={className} onClick={onClick} />;
}

Passing Props to Children Components

props are immutable (read-only) and one-way: data flows down from the parent to the child

  • If a child component needs to modify a prop, the parent must pass a callback function as a reference
// Parent component
function Parent() {
  const [count, setCount] = React.useState(0);

  // handleIncrement allows the child component to modify the count
  const handleIncrement = () => {
    setCount(current => current + 1);
  };

  return <Child count={count} onIncrement={handleIncrement} />;
}

// Child component
function Child({ count, onIncrement }) {
  count = count + 1;  // ❌ this won't work; props are read-only
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={onIncrement}>Increment</button>
    </div>
  );
}

----------------------------------------------------------------
// Simplified version using setCount directly
function Parent() {
  const [count, setCount] = React.useState(0);
  return <Child count={count} setCount={setCount} />;
}

function Child({ count, setCount }) {
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(current => current + 1)}>Increment</button>
    </div>
  );
}

Nesting Components with props.children

props.children returns everything between a component's opening and closing JSX tags

  • Useful for reusing components while letting the parent control the content
// Parent component
function Grid() {
  return (
    <grid>
      <Card>
        <h5>Image 1</h5>
        <img src="/image1.jpg" />
      </Card>
      <Card>
        <h5>Image 2</h5>
        <img src="/image2.jpg" />
      </Card>
    </grid>
  );
}

// Child component
// Content inside Card depends on parent; makes lower-level components reusable
function Card(props) {
  return (
    <div className="grid-item">
      {props.children}
    </div>
  );
}

Setting Default Values for props

Default values can be set for props if none are passed, using three methods:

  • Add a defaultProps static property
  • function Card({ src = '/defaultImage.jpg' }) {
      return <img src={src} />;
    }
    
    Card.defaultProps = {
      src: '/defaultImage.jpg',
    };
  • Add the default value directly in the function arguments
  • function Card({ src = '/defaultImage.jpg' }) {
      return <img src={src} />;
    }
  • Set the default value inside the function body using destructuring
  • function Card(props) {
      const { src = '/defaultImage.jpg' } = props;
      return <img src={src} />;
    }

Suspense API and use Hook

The Suspense component in React lets you handle many nested async operations cleanly. Until the async functions finish, the component will show a subsitute using the attribute fallback

  • Primarily used with the use hook to tell when the async functions are finished
  • While in the suspended state (i.e. not finished/still loading), the children elements are not rendered yet
  • It stays suspended until all nested async functions are finished
  • Documentation: react.dev/reference/react/Suspense

The use Hook

resolvedValue = use( Promise )

  • As of React 18+, the hook only accepts cached Promise or Promises that came from a Server component
  • Documentation: react.dev/reference/react/use
  • Notes on Promises in javascript: link
import { Suspense } from 'react';

export default function MyPage(){
  return (
    <Suspense key={index} fallback={<div>Loading...</div>}>       
      <LazyImage src={src} className={styles.img} />
    </Suspense>
  );
}
---------------------------------------------------------------
// Using cached Promises with Suspense
'use client';
import React, { use } from 'react';

const imgCache = new Map();

function loadImage(src) {
    if (!imgCache.has(src)) {
        imgCache.set(
            src,
            new Promise((resolve, reject) => {
                const img = new window.Image();
                img.src = src;
                img.onload = resolve;
                img.onerror = reject;
            })
        );
    }
    return imgCache.get(src);
}

export default function LazyImage({ className, src }) {
    use(loadImage(src));

    return <img src={src} className={className}/>
}

Event Listener & Handler

Event handlers are custom callback functions with the naming convention that they start with 'handle'

Event listeners are used in the same way as their HTML counterpart

Naming Convention

  • React uses camelCase to name their events - onClick instead of click
  • The custom callback function that handles the event commonly starts with 'handle'
  • For 'click' event, the callback function would be named handleClick
  • For 'hover' event, the callback function would be named handleHover
// JSX
function SubmitButton() {
  function handleClick() {
    alert('Submission Successful.');
  }
  return <button onClick={handleClick}>Submit</button>;
}

Common Event React Supports

  • Mouse Events
    • onClick : Mouse click on an element
    • onDoubleClick : Double-click on an element
    • onMouseDown / onMouseUp : Mouse button pressed/released
    • onMouseMove : Mouse moves over an element
    • onMouseEnter / onMouseLeave : Mouse enters/leaves (no bubbling)
    • onMouseOver / onMouseOut : Mouse enters/leaves (bubbles)
    • onContextMenu : Right-click context menu
  • Pointer Events
    • onPointerDown, onPointerUp, onPointerMove : Pointer pressed, released, moved
    • onPointerEnter, onPointerLeave : Pointer enters/leaves (no bubbling)
    • onPointerOver, onPointerOut : Pointer enters/leaves (bubbles)
    • onPointerCancel : Pointer interaction canceled
  • Keyboard Events
    • onKeyDown : Key is pressed down
    • onKeyPress : Key is pressed (deprecated, prefer onKeyDown)
    • onKeyUp : Key is released
  • Form & Input Events
    • onSubmit : Form submission
    • onChange : Input/select value changes
    • onInput : Input value changes (real-time)
    • onFocus / onBlur : Element gains or loses focus
    • onInvalid : Form validation fails
    • onReset : Form reset
  • Clipboard Events
    • onCopy, onCut, onPaste : Clipboard interactions
  • Drag & Drop Events
    • onDragStart, onDrag, onDragEnd : Drag starts, moves, ends
    • onDragEnter, onDragOver, onDragLeave,onDrop : Drag target interactions
  • Touch Events
    • onTouchStart, onTouchMove, onTouchEnd,onTouchCancel : Touch interactions
  • Composition Events
    • onCompositionStart, onCompositionUpdate,onCompositionEnd : IME text input handling
  • Media Events
    • onPlay, onPause, onEnded : Media playback state
    • onVolumeChange, onTimeUpdate, onProgress : Media volume, time, and loading progress
    • onLoadedData, onLoadedMetadata, onCanPlay,onCanPlayThrough : Media readiness events
    • onStalled, onSuspend, onWaiting : Media buffering states
  • Animation & Transition Events
    • onAnimationStart, onAnimationEnd,onAnimationIteration : CSS animations
    • onTransitionEnd : CSS transition ends
  • Wheel & Scroll Events
    • onWheel : Mouse wheel scroll
    • onScroll : Scroll event on an element

More events: react.dev/reference/react-dom/components/common


Communicating Between Components

  • Using props: parent to children
  • Using ref: parent → child 1 → parent → child 2
  • Using context: as long as the components are wrapped around the context provider
  • Using zustand store: global

Portal