React0-1Event Handling & React State

3.1 Responding to Events in React

Basic Example of Event Handling in React

src/App.jsx
function App() {
 
  const handleClick = (e) => {
    // Log the event object when the button is clicked
    console.log(e)
    // Alert showing the message
    alert('I was clicked')
  }
 
  const handleMouseMove = (e) => {
    // Log a message when the mouse moves over the button
    console.log('Mouse is moving')
  }
 
  return (
    <>
      {/* Button that triggers both the click and mouse move events */}
      <button onClick={handleClick} onMouseMove={handleMouseMove}>Click Me</button>
    </>
  )
}
 
export default App

Form Handling Example in React

src/App.jsx
import './App.css'
 
function App() {
 
  // Function to handle form submission
  const handleFormSubmit = (e) => {
    // Prevent the default form submission behavior (page reload)
    e.preventDefault()
    // Log the event object to the console (which includes form data)
    console.log(e)
    // Log a message when the form is submitted
    console.log("form was submitted")
  }
 
  // Function to handle username input change
  const handleUsernameChange = (e) => {
    // Capture the value of the input field as the username
    const username = e.target.value;
    // Log the username to the console
    console.log(username)
  }
 
  return (
    <>
      {/* Form with a submit button */}
      <form onSubmit={handleFormSubmit}>
        
        {/* Label and text input for username */}
        <label htmlFor="name">Name</label>
        <input 
          id='name' 
          type="text" 
          name='username' 
          onChange={handleUsernameChange}
        />
 
        {/* Label and password input */}
        <label htmlFor="password">Password</label>
        <input type="password" name="password" id="password" />
 
        {/* Submit button */}
        <button>Submit</button>
      </form>
    </>
  )
}
 
export default App

Form Handling Example in React with onClick (Without Using it)

src/App.jsx
import './App.css'
 
function App() {
 
  // Function to handle form submission
  const handleFormSubmit = (e) => {
    // Prevent the default form submission behavior (page reload)
    e.preventDefault()
    // Log the event object to the console (which includes form data)
    console.log(e)
    // Log a message when the form is submitted
    console.log("form was submitted")
  }
 
  // Function to handle username input change
  const handleUsernameChange = (e) => {
    // Capture the value of the input field as the username
    const username = e.target.value;
    // Log the username to the console
    console.log(username)
  }
 
  return (
    <>
      {/* Form with a submit button */}
      <form onSubmit={handleFormSubmit}>
        
        {/* Label and text input for username */}
        <label htmlFor="name">Name</label>
        <input 
          id='name' 
          type="text" 
          name='username' 
          onChange={handleUsernameChange}
        />
 
        {/* Label and password input */}
        <label htmlFor="password">Password</label>
        <input type="password" name="password" id="password" />
 
        {/* Submit button */}
        {/* The form submission is handled by onSubmit, no need for onClick */}
        <button>Submit</button>
      </form>
    </>
  )
}
 
export default App

Form Handling Example in React with onClick

src/App.jsx
import './App.css'
 
function App() {
 
  // Function to handle form submission
  const handleFormSubmit = (e) => {
    // Prevent the default form submission behavior (page reload)
    e.preventDefault()
    // Log a message when the form is submitted
    console.log("form was submitted")
  }
 
  // Function to handle username input change
  const handleUsernameChange = (e) => {
    // Capture the value of the input field as the username
    const username = e.target.value;
    // Log the username to the console
    console.log(username)
  }
 
  return (
    <>
      {/* Form with a submit button */}
      <form>
        
        {/* Label and text input for username */}
        <label htmlFor="name">Name</label>
        <input 
          id='name' 
          type="text" 
          name='username' 
          onChange={handleUsernameChange}
        />
 
        {/* Label and password input */}
        <label htmlFor="password">Password</label>
        <input type="password" name="password" id="password" />
 
        {/* Submit button with onClick handler */}
        <button onClick={handleFormSubmit}>Submit</button>
      </form>
    </>
  )
}
 
export default App

Form Handling Example in React with Both onSubmit and onClick

src/App.jsx
import './App.css'
 
function App() {
 
  // Function to handle form submission
  const handleFormSubmit = (e) => {
    // Prevent the default form submission behavior (page reload)
    e.preventDefault()
    // Log a message when the form is submitted
    console.log("form was submitted")
  }
 
  // Function to handle username input change
  const handleUsernameChange = (e) => {
    // Capture the value of the input field as the username
    const username = e.target.value;
    // Log the username to the console
    console.log(username)
  }
 
  // Function to handle the submit button click
  const handleButtonClick = (e) => {
    // Log a message when the submit button is clicked
    console.log('Submit button clicked')
  }
 
  return (
    <>
      {/* Form with both onSubmit and onClick handlers */}
      <form onSubmit={handleFormSubmit}>
        
        {/* Label and text input for username */}
        <label htmlFor="name">Name</label>
        <input 
          id='name' 
          type="text" 
          name='username' 
          onChange={handleUsernameChange}
        />
 
        {/* Label and password input */}
        <label htmlFor="password">Password</label>
        <input type="password" name="password" id="password" />
 
        {/* Submit button with onClick handler */}
        <button onClick={handleButtonClick}>Submit</button>
      </form>
    </>
  )
}
 
export default App

React Event Handling with Child Components

src/Event2.jsx
import React from 'react'
 
const ChildA = (props) => {
    const steps = 5
    return (
      <button onClick={() => props.handler(steps)}>Click Me Child A</button>
    )
}
 
const ChildB = (props) => {
    return (
      <button onClick={() => props.handler()}>Click Me Child B</button>
    )
}
 
const Event2 = () => {
    const walk = (steps) => {
        alert('I am walking ' + steps + " steps")
    }
 
    const eating = () => {
        alert('I am eating')
    }
 
    return (
        <div>
            <ChildA handler={walk}/>
            <ChildB handler={eating}/>
        </div>
    )
}
 
export default Event2

React Event Handling with Child Components and Event Propagation

src/Event2.jsx
import React from 'react'
 
const ChildA = (props) => {
    const steps = 5
    return (
      <button onClick={(e) => props.handler(steps, e)}>Click Me Child A</button>
    )
}
 
const ChildB = (props) => {
    return (
      <button onClick={(e) => props.handler(e)}>Click Me Child B</button>
    )
}
 
const Event2 = () => {
    const walk = (steps, e) => {
        alert('I am walking ' + steps + " steps")
        e.stopPropagation()
    }
 
    const eating = (e) => {
        alert('I am eating')
        e.stopPropagation()
    }
 
    return (
        <div onClick={() => alert('I am parent')}>
            <ChildA handler={walk}/>
            <ChildB handler={eating}/>
        </div>
    )
}
 
export default Event2

3.2 Introduction to State in React

Counter Component

src/Counter.jsx
import React from 'react';
 
// This is the Counter component
const Counter = () => {
  // Initialize the counter variable
  let counter = 10;
 
  // Function to increment the counter value
  const incrementCounter = () => {
    counter++;  // Increment counter by 1
    console.log(counter); // Log the current value of the counter to the console
  };
 
  console.log("Rendered"); // Log that the component has rendered
 
  return (
    <div>
      {/* Display the current value of the counter */}
      <p>{counter}</p>
      
      {/* Button to trigger the incrementCounter function when clicked */}
      <button onClick={incrementCounter}>Increase Counter</button>
    </div>
  );
};
 
// Export the Counter component for use in other files
export default Counter;

Counter Component with useState

import React, { useState } from 'react';
 
// This is the Counter component
const Counter = () => {
  // Declare state variable 'counter' and the function to update it: 'setCounter'
  const [counter, setCounter] = useState(0); // Initial counter value is 0
 
  // Function to increment the counter value by 1
  const incrementCounter = () => {
    setCounter(counter + 1); // Update the state with the new counter value
    // console.log(counter); // Uncomment to log the current counter value
  };
 
  console.log("Rendered"); // Log whenever the component renders (useful for debugging)
 
  return (
    <div>
      {/* Display the current value of the counter */}
      <p>{counter}</p>
      
      {/* Button that triggers the incrementCounter function when clicked */}
      <button onClick={incrementCounter}>Increase Counter</button>
    </div>
  );
};
 
// Export the Counter component for use in other files
export default Counter;
 
 
 
import React from 'react'
import Counter from './Counter'
 
const StateIntro = () => {
  return (
    <div>
        <Counter />
        <Counter />
    </div>
  )
}
 
export default StateIntro

Counter Component with Conditional Rendering

src/Counter.jsx
import React, { useState } from 'react';
 
// This is the Counter component
const Counter = () => {
  // Declare state variable 'counter' and the function to update it: 'setCounter'
  const [counter, setCounter] = useState(0); // Initial counter value is 0
 
  // Function to increment the counter value by 1
  const incrementCounter = () => {
    setCounter(counter + 1); // Update the state with the new counter value
    // console.log(counter); // Uncomment to log the current counter value
  };
 
  // Function to decrement the counter value by 1
  const decrementCounter = () => {
    setCounter(counter - 1); // Update the state with the new counter value
    // console.log(counter); // Uncomment to log the current counter value
  };
 
  console.log("Rendered"); // Log whenever the component renders (useful for debugging)
 
  return (
    <div>
      {/* Display the current value of the counter */}
      <p>{counter}</p>
      
      {/* Conditionally render the decrement button if the counter is greater than 0 */}
      {counter > 0 && <button onClick={decrementCounter}>Decrement Counter</button>}
      
      {/* Conditionally render the increment button if the counter is less than 5 */}
      {counter < 5 && <button onClick={incrementCounter}>Increase Counter</button>}
    </div>
  );
};
 
// Export the Counter component for use in other files
export default Counter;
src/StateIntro.jsx
import React from 'react';
import Counter from './Counter'; // Import the Counter component
import { useState } from 'react';
 
// This is the StateIntro component
const StateIntro = () => {
  // Declare a state variable to toggle visibility of the Counter component
  const [showCounter, setShowCounter] = useState(true);
 
  return (
    <div>
      {/* Conditionally render the Counter component based on 'showCounter' */}
      {showCounter && <Counter />}
      
      {/* Button to toggle the 'showCounter' state between true and false */}
      <button onClick={() => setShowCounter(!showCounter)}>
        Toggle Counter
      </button>
    </div>
  );
};
 
// Export the StateIntro component
export default StateIntro;

React’s Rendering Process

When the Counter component is added to the screen (by toggling showCounter to true), React will mount the component. This means React creates an instance of the component, attaches it to the DOM, and ensures it’s displayed on the screen.

When the Counter component is hidden (by toggling showCounter to false), React will unmount the component. Unmounting means React removes the component from the DOM, freeing up memory and resources until the component is rendered again.

This efficient system is part of React’s virtual DOM and reconciliation process. The virtual DOM is an in-memory representation of the actual DOM, and React uses it to minimize the number of updates to the real DOM, only applying necessary changes when the state or props change. This results in better performance because React avoids unnecessary re-renders or manipulations of the DOM, especially when working with large and complex UIs.

By efficiently managing which components need to be mounted, updated, or unmounted, React ensures smoother user experiences, even in complex applications.

3.3 How does React State work Internally?

React State Update Behavior

import React, { useState } from 'react';
 
// This is the Counter component
const Counter = () => {
  // Declare a state variable 'counter' with initial value 0
  const [counter, setCounter] = useState(0); 
 
  // Function to increment the counter multiple times
  const incrementCounter = () => {
    // The setCounter method updates the state, but React batches these updates
    // It does not immediately reflect changes in the `counter` state until after all updates are processed.
 
    // Increment counter by 1 multiple times
    setCounter(counter + 1); // First increment (0 + 1 = 1)
    setCounter(counter + 1); // Second increment (0 + 1 = 1)
    setCounter(counter + 1); // Third increment (0 + 1 = 1)
    setCounter(counter + 1); // Fourth increment (0 + 1 = 1)
 
    // Since React batches updates, the final value of `counter` remains 1
    // and does not immediately reflect the updated value of 4 increments.
    console.log(counter); // Logs 0, because React batches the updates before reflecting them
  };
 
  return (
    <div>
      {/* Display the current value of the counter */}
      <p>{counter}</p>    
 
      {/* Button that triggers the incrementCounter function when clicked */}
      <button onClick={incrementCounter}>Increase Counter</button>
    </div>
  );
};
 
export default Counter;

Counter Component with Local and State Variables

import React from 'react'
import { useState } from 'react';
 
const Counter = () => {
  // Declare a state variable 'counter' with initial value 0
  const [counter, setCounter] = useState(0);
 
  // Local variable 'x' that is not tied to React's re-render cycle
  let x = 0;
 
  // Function to increment the counter multiple times
  const incrementCounter = () => {
    // Multiple calls to setCounter - React batches updates, so the state does not immediately reflect changes
    setCounter(counter + 1); // First increment (0 + 1 = 1)
    setCounter(counter + 1); // Second increment (0 + 1 = 1)
    setCounter(counter + 1); // Third increment (0 + 1 = 1)
    setCounter(counter + 1); // Fourth increment (0 + 1 = 1)
 
    // Local variable 'x' is incremented immediately
    x++;
 
    // Logs the local variable 'x', which gets updated immediately within the function
    console.log("x=", x); // Logs 'x= 1'
 
    // Logs the state variable 'counter', which doesn't reflect the updated value immediately
    console.log("counter=", counter); // Logs 'counter= 0' because React batches state updates
  };
 
  return (
    <div>
      <p>{counter}</p>  {/* Displays the current counter value */}
      <button onClick={incrementCounter}>Increase Counter</button>  {/* Button to trigger increment */}
    </div>
  );
};
 
export default Counter;

Counter Component with Local and State Variables

import React from 'react'
import { useState } from 'react';
 
const Counter = () => {
  // State variable
  const [counter, setCounter] = useState(0);
 
  // Local variable
  let x = 0;
 
  // Increment counter function
  const incrementCounter = () => {
    // These two state updates will be batched by React
    setCounter(counter + 1); // set counter to 1 (0 + 1)
    setCounter(counter - 1); // set counter to -1 (0 - 1)
 
    // Local variable 'x' is updated immediately
    x++;
 
    // Logging local variable and state variable
    console.log("x=", x); // Logs '1'
    console.log("counter=", counter); // Logs '0' due to batching
  }
 
  return (
    <div>
      {/* Display the current value of counter */}
      <p>{counter}</p>    
 
      {/* Button that triggers incrementCounter on click */}
      <button onClick={incrementCounter}>Increase Counter</button>
    </div>
  );
}
 
export default Counter;

3.4 Sharing State Data Between Components

Parent to Child

src/Parent.jsx
import React from 'react'
import Child from './Child'
import { useState } from 'react'
 
const Parent = () => {
    const [count, setCount] = useState(0)
 
    const handleParentButton = () => {
        setCount(prevCount => prevCount + 1)
    }
  return (
    <div>
        <p>Inside Parent: {count}</p>
        <button onClick={handleParentButton}>Parent Button</button>
        <Child count={count}/>
    </div>
  )
}
 
export default Parent
src/Child.jsx
import React from 'react'
 
const Child = (props) => {
  return (
    <div>Child: {props.count}</div>
  )
}
 
export default Child

Child to parent

3.5 Handling Object and Array State in React, Part - 1

3.6 Handling Object and Array State in React, Part - 2

3.7 Styling the Notes App using Tailwind CSS