Unit 3 - Notes

INT252

Unit 3: Events, States, Component Lifecycle and Hooks

1. Event Handling in React

React events are named using camelCase rather than lowercase. With JSX, you pass a function as the event handler, rather than a string.

Key Differences from HTML

  1. Syntax:
    • HTML: <button onclick="activateLasers()">
    • React: <button onClick={activateLasers}>
  2. Prevent Default Behavior:
    • In HTML, you can return false to prevent default behavior.
    • In React, you must call preventDefault explicitly.

Synthetic Events

React implements a "Synthetic Event" system. It is a cross-browser wrapper around the browser’s native event. This ensures that events work identically across Chrome, Firefox, Safari, etc.

  • Accessing native events: The synthetic event is passed as an argument to the handler. You can access the underlying browser event via event.nativeEvent.

Handling Events in Functional Components

JSX
function Form() {
  function handleSubmit(e) {
    e.preventDefault(); // Prevents page reload
    console.log('You clicked submit.');
  }

  return (
    <form onSubmit={handleSubmit}>
      <button type="submit">Submit</button>
    </form>
  );
}

Passing Arguments to Event Handlers

To pass an extra parameter to an event handler, use an arrow function or bind.

JSX
// Using Arrow Function
<button onClick={(e) => deleteRow(id, e)}>Delete Row</button>


2. State and Components

Understanding State

State is a built-in React object that acts as a data store for components. When the state object changes, the component re-renders.

  • Mutable: Unlike Props (which are read-only), State is mutable.
  • Local: State is local to the component and usually hidden from other components.

Stateless vs. Stateful Components

Feature Stateless Component (Presentational/Dumb) Stateful Component (Container/Smart)
Primary Function Focuses on UI (how things look). Focuses on Logic (how things work).
Data Source Receives data via props. Manages its own data via state.
Lifecycle Historically did not use lifecycle methods (prior to Hooks). Uses lifecycle methods (or Hooks) to fetch data.
Reusability Highly reusable. Less reusable due to specific logic coupling.

Creating State (The Modern Approach with Hooks)

While class components use this.state and this.setState, modern React uses the useState hook.

JSX
import React, { useState } from 'react';

function Counter() {
  // "count" is the state variable
  // "setCount" is the function to update it
  // 0 is the initial value
  const [count, setCount] = useState(0);

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


3. Component Lifecycle

The lifecycle describes the phases a component goes through from creation to deletion. While Hooks handle this in functional components, understanding the lifecycle phases is crucial.

Phase 1: Mounting

These methods are called when an instance of a component is being created and inserted into the DOM.

  1. constructor() (Class only): Used to initialize state and bind event handlers.
  2. static getDerivedStateFromProps(): Invoked right before calling the render method. Used when state depends on changes in props.
  3. render(): The only required method. Returns the JSX.
  4. componentDidMount(): Invoked immediately after a component is mounted.
    • Use Case: Network requests (API calls), subscriptions, DOM manipulation.

Phase 2: Updating

Caused by changes to props or state.

  1. static getDerivedStateFromProps(): Called again on update.
  2. shouldComponentUpdate(): Returns a boolean. Determines if React should skip rendering (performance optimization).
  3. render(): Re-renders the UI.
  4. getSnapshotBeforeUpdate(): Capture information from the DOM (e.g., scroll position) before it changes.
  5. componentDidUpdate(): Invoked immediately after updating occurs.
    • Use Case: Network requests based on state changes.

Phase 3: Unmounting

Called when a component is being removed from the DOM.

  1. componentWillUnmount(): Cleanup phase.
    • Use Case: Canceling network requests, removing event listeners, clearing timers/intervals.

4. React Hooks Basics

Hooks were introduced in React 16.8. They allow you to use state and other React features without writing a class.

Rules of Hooks

  1. Only Call Hooks at the Top Level: Do not call Hooks inside loops, conditions, or nested functions. They must execute in the exact same order during every render.
  2. Only Call Hooks from React Functions: Call them from React functional components or custom Hooks. Do not call them from regular JavaScript functions.

5. Essential Built-in Hooks

useState

Allows functional components to have local state.

  • Syntax: const [state, setState] = useState(initialState);
  • Functional Updates: If the new state depends on the previous state, pass a function to the setter.
    JSX
        setCount(prevCount => prevCount + 1);
        

useEffect

Performs side effects in function components. It serves the same purpose as componentDidMount, componentDidUpdate, and componentWillUnmount unified into a single API.

Syntax: useEffect(function, dependencyArray)

  1. Run on every render: (No dependency array)
    JSX
        useEffect(() => { console.log('Runs every render'); });
        
  2. Run only on Mount: (Empty array [])
    JSX
        useEffect(() => { console.log('Runs once on mount'); }, []);
        
  3. Run on Dependency Change: (Array with variables [prop, state])
    JSX
        useEffect(() => { console.log('Runs when count changes'); }, [count]);
        
  4. Cleanup (Unmount): Return a function.
    JSX
        useEffect(() => {
          const timer = setInterval(tick, 1000);
          return () => clearInterval(timer); // Cleanup function
        }, []);
        

6. Context and References

useContext

Accepts a context object (returned from React.createContext) and returns the current context value. It solves the "Prop Drilling" problem (passing data through many layers of components).

JSX
const ThemeContext = React.createContext('light');

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button style={{ background: theme === 'dark' ? '#333' : '#CCC' }}>I am styled</button>;
}

useRef

Returns a mutable ref object whose .current property is initialized to the passed argument.

  • Use Case 1: Accessing DOM elements.
    JSX
        const inputEl = useRef(null);
        const onButtonClick = () => {
          inputEl.current.focus(); // Focus the input element directly
        };
        return <input ref={inputEl} />;
        
  • Use Case 2: Storing mutable values. Values stored in refs persist across renders but do not trigger a re-render when changed.

7. Advanced Hooks

useReducer

An alternative to useState for complex state logic (similar to Redux). It accepts a reducer function of type (state, action) => newState and returns the current state paired with a dispatch method.

JSX
const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment': return {count: state.count + 1};
    case 'decrement': return {count: state.count - 1};
    default: throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
    </>
  );
}

useMemo (Performance Optimization)

Memoizes a computed value. It only recomputes the memoized value when one of the dependencies has changed. This avoids expensive calculations on every render.

JSX
const expensiveValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

useCallback (Performance Optimization)

Memoizes a callback function. It returns a memoized version of the callback that only changes if one of the dependencies has changed. Useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders.

JSX
const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

Difference: useMemo returns a result (value), useCallback returns a function.


8. Custom Hooks

A Custom Hook is a JavaScript function whose name starts with "use" and that may call other Hooks. It allows you to extract component logic into reusable functions.

Scenario: You have duplicate logic in two components that fetches data from an API.

Creating the Hook (useFetch.js):

JSX
import { useState, useEffect } from 'react';

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch(url)
      .then((res) => res.json())
      .then((data) => {
        setData(data);
        setLoading(false);
      });
  }, [url]);

  return { data, loading };
}

export default useFetch;

Using the Hook:

JSX
function Users() {
  const { data, loading } = useFetch('https://api.example.com/users');

  if (loading) return <p>Loading...</p>;
  return <ul>{data.map(user => <li key={user.id}>{user.name}</li>)}</ul>;
}