React Content
LAST UPDATED: August 25, 2025

React Hello World

Hello Everyone!

In this tutorial, we will learn how to write a basic Hello World program in React.


// Example: Display Hello World using React

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

function App() {
  return (
    <h1>Hello World! Welcome to Tutorials!</h1>
  );
}

// Rendering App component into root element
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);

  

Explanation:

Let's break down what we have done in the above code.

1. App Component

We created a functional component called App. A component in React is a JavaScript function that returns UI elements using JSX.

2. JSX

The line <h1>Hello World! Welcome to Tutorials!</h1> is written in JSX. JSX looks like HTML but is actually JavaScript syntax for creating UI elements.

3. ReactDOM.createRoot()

This method tells React where to render our component in the HTML. In this case, we target an element with id="root" in index.html.

4. Running the Code

- Create a new React project using Vite or Create React App.
- Replace the default App.js content with the above code.
- Run npm start (CRA) or npm run dev (Vite).
- Open http://localhost:3000 or http://localhost:5173 in your browser.


Introduction to React

1.1 What is React?

React is a JavaScript library used for building user interfaces (UI). It was developed by Facebook and is now one of the most popular tools for web development.

Imagine your web page as a big house. React allows you to build this house using many small, reusable blocks called components. For example, a button, a navbar, or a profile card can each be a component.

React updates only the parts of the page that change (using something called the Virtual DOM) instead of reloading the whole page. This makes React apps fast and efficient.

// Example: A simple React component
import React from "react";

function App() {
  return <h1>Hello, React!</h1>;
}

export default App;
    

1.2 Features of React

React has some special features that make it powerful:

  • Component-based: Break your UI into small, reusable pieces.
  • Virtual DOM: Updates only the changed part, making apps faster.
  • Declarative: You tell React what you want, and it handles the how.
  • JSX: Lets you write HTML inside JavaScript code.
  • Unidirectional Data Flow: Data flows in one direction, so apps are predictable.
  • Cross-platform: With React Native, you can even build mobile apps!
// Example: Rendering a list of features
function Features() {
  return (
    <ul>
      <li>Component-based</li>
      <li>Virtual DOM</li>
      <li>Declarative</li>
    </ul>
  );
}
    

1.3 Library or Framework?

React is a library, not a full framework. A framework (like Angular) tells you how to build the whole app with strict rules. A library (like React) gives you tools only for UI, and you can choose other tools as you like.

For example, React doesn’t handle routing (navigating pages) by default. If you need that, you install react-router.

// Example: Adding React Router for navigation
import { BrowserRouter, Route } from "react-router-dom";

function App() {
  return (
    <BrowserRouter>
      <Route path="/" element={<h1>Home</h1>} />
    </BrowserRouter>
  );
}
    

1.4 React & Other Frameworks

React is often compared with Angular and Vue:

  • React: A library for UI, very flexible, needs extra libraries for other tasks.
  • Angular: A full framework by Google, comes with everything built-in (but more complex).
  • Vue: Easy to learn, mixes ideas from both React and Angular.

Think of React as a "toolbox" where you pick the tools you need, while Angular is a "ready-made kit" with everything inside.

Task: Compare React and Angular: Which one is easier for beginners and why?

1.5 Installation & Setup

Before you can use React, you need some tools:

  1. Install Node.js and npm
    - Go to nodejs.org and download the LTS version.
    - Installing Node will also install npm (Node Package Manager). - You can check installation by running in terminal:
    node -v
    npm -v
  2. Install a Code Editor
    - The most popular one is VS Code.
  3. Create Your First React App
    The easiest way is with Create React App:
    npx create-react-app my-app
    cd my-app
    npm start
    This will open a browser at http://localhost:3000 with your React app running 🎉
  4. Alternative Setup
    - You can also use Vite (faster and modern) with:
    npm create vite@latest my-app
    cd my-app
    npm install
    npm run dev

After setup, open App.js inside your project and replace the code with:

function App() {
  return <h1>Hello React!</h1>;
}

export default App;
    

Task: Create a new React project, open App.js, and change the text to show your name.


JSX (JavaScript XML)

2.1 What is JSX?

JSX stands for JavaScript XML. It allows you to write HTML-like syntax directly inside JavaScript. JSX makes it easy to describe how the UI should look and is commonly used in React.

Although browsers don’t understand JSX directly, tools like Babel convert JSX into regular JavaScript that browsers can run.

// Example: JSX inside React
const element = <h1>Hello, JSX!</h1>;

    

Task: Write a JSX element that displays your name inside an <h2> tag.

2.2 Embedding Expressions

In JSX, you can embed JavaScript expressions inside curly braces { }. This makes it possible to dynamically display variables, perform calculations, or even call functions.

const name = "Raj";
const age = 21;

const element = <p>My name is {name} and I am {age} years old.</p>;

    

Task: Create a JSX element that shows the result of 10 + 5 inside a paragraph.

2.3 JSX vs HTML Differences

JSX looks like HTML, but there are some differences:

  • class in HTML → className in JSX
  • for in HTML → htmlFor in JSX
  • All tags must be closed (e.g., <br /> instead of <br>)
  • JSX can only return one root element
// Example
const element = <div className="container">
  <label htmlFor="inputName">Name:</label>
  <input id="inputName" />
</div>;

    

Task: Fix this invalid JSX: <h1>Hello</h1><p>World</p>.

2.4 Attributes in JSX

In JSX, attributes look like HTML but follow camelCase for multi-word names.

// Example with attributes
const image = <img src="profile.jpg" alt="Profile" width="200" />;

    

Task: Create a JSX element for a link with href="https://reactjs.org" and text "Learn React".

2.5 JSX with Components

JSX is usually written inside components. A component is a function that returns JSX.

// Example: Component with JSX
function Welcome() {
  return <h1>Welcome to React!</h1>;
}

    

Task: Create a component called Greeting that displays "Hello, User!".

2.6 Conditional Rendering in JSX

You can use JavaScript conditions (like if, ternary operator) inside JSX.

const isLoggedIn = true;

const message = <h1>{isLoggedIn ? "Welcome Back!" : "Please Sign In"}</h1>;

    

Task: Write JSX that shows "Adult" if age ≥ 18, otherwise "Minor".

2.7 Lists & Keys in JSX

You can display lists using the map() function. Each item should have a unique key for React to track changes.

const hobbies = ["Reading", "Gaming", "Traveling"];

const list = (
  <ul>
    {hobbies.map((hobby, index) => (
      <li key={index}>{hobby}</li>
    ))}
  </ul>
);

    

Task: Create a list of your 3 favorite foods using JSX.

2.8 JSX and Styling

You can style JSX elements using style (with an object) or by adding CSS classes.

// Inline style
const element = <h1 style={{ color: "blue", fontSize: "24px" }}>Styled Text</h1>;

    
// With CSS class
const element = <p className="highlight">Hello CSS</p>;

    

Task: Create a red-colored heading using inline styles in JSX.

2.9 JSX Best Practices

  • Always close tags (e.g., <img />).
  • Keep components small and focused.
  • Use className instead of class.
  • Wrap multiple elements inside one parent (or use <></> fragment).
  • Use keys when rendering lists.

React Components

Components are the building blocks of React applications. A component is a reusable piece of code that defines how a part of the UI should look and behave. Components can be simple (just displaying text) or complex (handling state, logic, and interactions).

3.1 Functional Components

Functional components are simple JavaScript functions that return JSX. They are the most common type of components in modern React.

// Example Functional Component
function Welcome() {
  return <h1>Hello, Functional Component!</h1>;
}

    

Task: Create a functional component called Greeting that displays "Welcome, User!".

3.2 Class Components

Class components are ES6 classes that extend React.Component. They can hold state and use lifecycle methods (before React 16.8 hooks were introduced).

// Example Class Component
import React, { Component } from "react";

class Welcome extends Component {
  render() {
    return <h1>Hello from Class Component!</h1>;
  }
}

    

Task: Write a class component that displays your favorite hobby.

3.3 Pure Components

A Pure Component automatically prevents re-rendering if props and state don’t change. This improves performance by avoiding unnecessary updates.

import React, { PureComponent } from "react";

class Greeting extends PureComponent {
  render() {
    return <h2>Hello, {this.props.name}</h2>;
  }
}

    

3.4 Higher Order Components (HOC)

A Higher Order Component (HOC) is a function that takes a component as input and returns a new component with additional features. It is mainly used for reusing logic across multiple components.

// Example HOC
function withGreeting(WrappedComponent) {
  return function(props) {
    return (
      <div>
        <p>Hello from HOC!</p>
        <WrappedComponent {...props} />
      </div>
    );
  };
}

    

3.5 Stateless Components

Stateless components don’t manage their own state. They only receive data via props and render UI based on it.

function DisplayName(props) {
  return <p>My name is {props.name}</p>;
}

    

3.6 Stateful Components

Stateful components manage their own state. They can update and re-render based on user interaction or logic.

import React, { useState } from "react";

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

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

    

3.7 Controlled Components

In controlled components, form inputs are controlled by React state. The value of the input field is determined by the component’s state.

function ControlledInput() {
  const [text, setText] = useState("");

  return (
    <input value={text} onChange={(e) => setText(e.target.value)} />
  );
}

    

3.8 Uncontrolled Components

In uncontrolled components, form inputs store their own state internally, and you access them using refs.

import { useRef } from "react";

function UncontrolledInput() {
  const inputRef = useRef(null);

  function handleClick() {
    alert(inputRef.current.value);
  }

  return (
    <div>
      <input ref={inputRef} />
      <button onClick={handleClick}>Show Value</button>
    </div>
  );
}

    

3.9 Presentational Components

Presentational components are mainly for displaying UI. They don’t manage logic or state, only render what is passed via props.

function ProfileCard(props) {
  return (
    <div>
      <h2>{props.name}</h2>
      <p>{props.bio}</p>
    </div>
  );
}

    

3.10 Container Components

Container components handle data and pass it to presentational components. They separate logic from UI.

function ProfileContainer() {
  const user = { name: "Rajesh", bio: "React Developer" };

  return <ProfileCard name={user.name} bio={user.bio} />;
}

    

3.11 Render Props Components

A render props component is a technique for sharing code between components by using a function as a prop.

function DataProvider(props) {
  const data = "Hello from DataProvider!";
  return props.render(data);
}

// Usage
<DataProvider render={(data) => <p>{data}</p>} />

    

Props & State in React

In React, Props and State are two important concepts for managing data. - Props (short for properties) are used to pass data from parent to child components. - State is used to manage data within a component that can change over time.

4.1 Props Overview

Props allow components to receive data from their parent. They are read-only and cannot be modified by the child component.

// Example: Passing props
function Welcome(props) {
  return <h1>Hello, {props.name}!</h1>;
}

// Usage
<Welcome name="Rajesh" />

    

Task: Create a component Greeting that receives your name via props and displays it.

4.2 Default Props

Default props are used to set default values for props if none are provided by the parent component.

function Welcome(props) {
  return <h1>Hello, {props.name}!</h1>;
}

Welcome.defaultProps = {
  name: "Guest"
};

// Usage
<Welcome />  // Output: Hello, Guest!

    

Task: Add default props to a Profile component for name and age.

4.3 Props Validation

Props validation ensures that components receive the correct type of data using prop-types.

import PropTypes from "prop-types";

function User(props) {
  return <p>Age: {props.age}</p>;
}

User.propTypes = {
  age: PropTypes.number.isRequired
};

    

Task: Create a User component that validates props for name (string) and age (number).

4.4 Props vs State

- Props: Passed from parent, read-only, used to configure a component. - State: Managed within the component, can be updated, used for dynamic behavior.

function Counter(props) {
  const [count, setCount] = React.useState(props.start);

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

// Usage
<Counter start={5} />

    

4.5 Props Drilling

Props drilling happens when props are passed down through multiple layers of components. It can make code harder to manage; solutions include Context API or Redux.

function Child({ name }) {
  return <p>Hello, {name}</p>;
}

function Parent({ name }) {
  return <Child name={name} />;
}

function App() {
  return <Parent name="Rajesh" />;
}

    

4.6 State Overview

State is used to store data that can change during the lifecycle of a component. Updating state re-renders the component automatically.

4.7 Setting State

You can set state when declaring it for the first time, usually using useState (for functional components).

const [message, setMessage] = React.useState("Hello");

    

4.8 Updating State

State is updated using the setter function returned by useState. Updating state re-renders the component with new values.

function Message() {
  const [text, setText] = React.useState("Hi");

  return (
    <div>
      <p>{text}</p>
      <button onClick={() => setText("Hello World!")} >Change</button>
    </div>
  );
}

    

4.9 State in Class Components

In class components, state is initialized in the constructor and updated with this.setState().

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  increment = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.increment}>+</button>
      </div>
    );
  }
}

    

4.10 State with Hooks (useState)

The useState hook allows functional components to use state. It returns an array with the current state and a function to update it.

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

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

    

Task: Build a counter using useState that increases and decreases the count.


Condi. Rendering & Mapping

In React, we often need to decide what to render based on certain conditions. This is called conditional rendering. Similarly, when displaying lists of data, we use the map() function. Let’s go through all the techniques step by step.

5.1 if/else Rendering

The most basic way is to use if/else statements to decide which component or element to render.

function Greeting(props) {
  if (props.isLoggedIn) {
    return <h1>Welcome back!</h1>;
  } else {
    return <h1>Please log in.</h1>;
  }
}

    

Task: Create a component that shows "Admin Dashboard" if isAdmin is true, otherwise "User Dashboard".

5.2 Ternary Operator Rendering

A shorthand way is using the ? : ternary operator. It is often used inside JSX.

function Greeting(props) {
  return (
    <h1>{props.isLoggedIn ? "Welcome Back!" : "Please Log In"}</h1>
  );
}

    

Task: Show "Online" if status is true, otherwise "Offline".

5.3 Logical && Rendering

The && operator is useful when you want to render something only if a condition is true. If the condition is false, nothing will be displayed.

function Messages(props) {
  return (
    <div>
      {props.unread > 0 && <p>You have {props.unread} new messages.</p>}
    </div>
  );
}

    

Task: Display "Discount Available!" only if hasDiscount is true.

5.4 Switch Case Rendering

Sometimes multiple conditions need different UI. In such cases, switch statements can be used.

function StatusMessage({ status }) {
  switch (status) {
    case "loading":
      return <p>Loading...</p>;
    case "success":
      return <p>Data loaded successfully!</p>;
    case "error":
      return <p>Error loading data.</p>;
    default:
      return <p>Unknown status.</p>;
  }
}

    

Task: Create a Weather component that shows messages based on "sunny", "rainy", or "cloudy".

5.5 Rendering Lists with map()

When rendering multiple elements (like a list), we use JavaScript’s map() function in JSX.

function TodoList(props) {
  return (
    <ul>
      {props.items.map(item => (
        <li key={item.id}>{item.text}</li>
      ))}
    </ul>
  );
}

    

Task: Create a list of your 3 favorite fruits using map().

5.6 Key Prop in Mapping

When rendering lists, React needs a unique key prop for each element. This helps React optimize re-rendering.

const numbers = [1, 2, 3, 4];
function NumberList() {
  return (
    <ul>
      {numbers.map(num => (
        <li key={num}>{num}</li>
      ))}
    </ul>
  );
}

    

Task: Render a list of user names with unique keys.

5.7 Nested Conditional Rendering

Sometimes you may need to combine multiple conditional rendering techniques. Example: showing different UI for multiple roles and login status.

function Dashboard({ isLoggedIn, role }) {
  if (!isLoggedIn) {
    return <p>Please log in.</p>;
  }

  return role === "admin" ? (
    <h1>Welcome, Admin!</h1>
  ) : role === "user" ? (
    <h1>Welcome, User!</h1>
  ) : (
    <h1>Welcome, Guest!</h1>
  );
}

    

Task: Build a component that shows different messages for "Student", "Teacher", and "Guest" roles depending on login status.


React Hooks

Hooks are special functions in React that let you "hook into" React features such as state, lifecycle methods, and context without writing class components. They make React code more readable, reusable, and concise. Below we’ll explore the most important hooks — their **purpose, syntax, and usage**.

6.1 useState

Purpose: Used to add state (data that changes over time) inside functional components. It returns an array → [currentValue, updaterFunction].

Syntax:

const [state, setState] = useState(initialValue);
import React, { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0); // state variable with initial value 0

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

  

Task: Build a counter with Increment and Decrement buttons using useState.

6.2 useEffect

Purpose: Handles side effects (fetching data, setting timers, adding event listeners). Runs after the component renders. Dependency array decides when it runs.

Syntax:

useEffect(() => {
  // effect code
  return () => {
    // optional cleanup code
  };
}, [dependencies]);

📌 Variations of useEffect

1. Run on every render (no dependency array)

useEffect(() => {
  console.log("Effect runs after every render!");
});

✅ Runs after every render (initial + updates).

2. Run only once (empty dependency array)

useEffect(() => {
  console.log("Effect runs only on mount!");

  fetch("https://jsonplaceholder.typicode.com/posts/1")
    .then(res => res.json())
    .then(data => console.log(data));
}, []);

✅ Runs only once after initial render (like componentDidMount).

3. Run when specific dependencies change

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

useEffect(() => {
  console.log("Runs only when count changes:", count);
}, [count]);

✅ Runs only when the value inside dependency array changes.

4. With Cleanup Function

useEffect(() => {
  const interval = setInterval(() => {
    console.log("Interval running...");
  }, 1000);

  // Cleanup before unmount or re-run
  return () => {
    clearInterval(interval);
    console.log("Interval cleared!");
  };
}, []);

✅ Useful for cleaning up subscriptions, timers, or event listeners.

📌 Example: Timer with Cleanup

import React, { useState, useEffect } from "react";

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds(s => s + 1);
    }, 1000);

    return () => clearInterval(interval); // cleanup when unmounted
  }, []);

  return <p>Seconds: {seconds}</p>;
}

Task: Use useEffect to Check User Status.

6.3 useContext

Purpose: Lets you access values from React’s Context API without passing props through every level. Great for themes, auth state, or global data.

Syntax:

const value = useContext(MyContext);
import React, { useContext, createContext } from "react";

const ThemeContext = createContext("light");

function ThemedButton() {
  const theme = useContext(ThemeContext); // consume context
  return <button>Current theme: {theme}</button>;
}

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <ThemedButton />
    </ThemeContext.Provider>
  );
}

  

Task: Create a Context for "User" and display the user name in a child component.

6.4 useReducer

Purpose: Alternative to useState when you have complex logic. Works with a reducer function like Redux (state, action).

Syntax:

const [state, dispatch] = useReducer(reducerFn, initialState);
import React, { useReducer } from "react";

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

function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: "increment" })}>+</button>
      <button onClick={() => dispatch({ type: "decrement" })}>-</button>
    </div>
  );
}

  

Task: Implement a todo list reducer with add/remove actions.

6.5 useLayoutEffect

Purpose: Like useEffect, but runs synchronously before the screen is painted. Useful for DOM measurements and animations.

Syntax:

useLayoutEffect(() => {
  // effect logic
  return () => { /* cleanup */ };
}, [dependencies]);
import React, { useLayoutEffect, useRef } from "react";

function Box() {
  const boxRef = useRef(null);

  useLayoutEffect(() => {
    console.log("Box width:", boxRef.current.offsetWidth);
  }, []);

  return <div ref={boxRef} style={{ width: "200px", height: "100px", background: "lightblue" }} />;
}

  

6.6 useRef

Purpose: Holds a mutable value that does not cause re-renders when changed. Commonly used to reference DOM nodes or store values between renders.

Syntax:

const refContainer = useRef(initialValue);
import React, { useRef } from "react";

function FocusInput() {
  const inputRef = useRef(null);

  const focusInput = () => {
    inputRef.current.focus(); // access DOM node
  };

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={focusInput}>Focus Input</button>
    </div>
  );
}

  

Task: Use useRef to create a stopwatch that saves the timer ID.

6.7 useMemo

Purpose: Optimizes performance by memoizing expensive calculations. Only recalculates when dependencies change.

Syntax:

const memoizedValue = useMemo(() => computeFn(), [dependencies]);
import React, { useState, useMemo } from "react";

function ExpensiveCalculation({ num }) {
  const calculation = useMemo(() => {
    console.log("Calculating...");
    return num * 2;
  }, [num]);

  return <p>Result: {calculation}</p>;
}

  

Task: Build a component that squares a number using useMemo and observe when recalculation happens.

6.8 useCallback

Purpose: Returns a memoized version of a function. Prevents unnecessary re-renders of child components when functions are passed as props.

Syntax:

const memoizedFn = useCallback(() => { ... }, [dependencies]);
import React, { useState, useCallback } from "react";

function Button({ onClick, children }) {
  console.log("Button rendered:", children);
  return <button onClick={onClick}>{children}</button>;
}

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

  const increment = useCallback(() => setCount(c => c + 1), []); // memoized

  return (
    <div>
      <p>Count: {count}</p>
      <Button onClick={increment}>Increment</Button>
    </div>
  );
}

  

Task: Pass a memoized callback to a child component and see the difference.

6.9 Custom Hooks

Purpose: Lets you extract and reuse stateful logic across components. They are plain functions that start with use.

Syntax:

function useCustomHook() { /* logic here */ return value; }
import { useState, useEffect } from "react";

function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    window.addEventListener("resize", handleResize);

    return () => window.removeEventListener("resize", handleResize);
  }, []);

  return width;
}

function App() {
  const width = useWindowWidth();
  return <p>Window width: {width}px</p>;
}

  

Task: Write a custom hook useOnlineStatus that tracks if the user is online/offline.


React Router DOM

React Router DOM is the standard routing library for React applications. It allows you to create single-page applications (SPA) with multiple views, enabling navigation without refreshing the page.

7.1 Introduction to React Router

React Router helps manage navigation between different components in a React app. Instead of reloading pages, it renders components based on the current URL.

npm install react-router-dom

Task: Install react-router-dom in your project.

7.2 BrowserRouter & HashRouter

These are two types of routers: - BrowserRouter: Uses HTML5 history API (clean URLs). - HashRouter: Uses # in URLs (useful for static hosting).

import { BrowserRouter, Routes, Route } from "react-router-dom";

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
      </Routes>
    </BrowserRouter>
  );
}

7.3 Routes and Route

Routes is a container for all route definitions. Route defines the mapping between a path and a component.

import { Routes, Route } from "react-router-dom";

function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/about" element={<About />} />
    </Routes>
  );
}

Task: Add routes for /home and /contact.

7.4 Link and NavLink

Link is used for navigation without reloading the page. NavLink is similar but adds active styles to the current link.

import { Link, NavLink } from "react-router-dom";

function Navbar() {
  return (
    <nav>
      <Link to="/">Home</Link>
      <NavLink to="/about" style={({ isActive }) => ({ color: isActive ? "red" : "black" })}>
        About
      </NavLink>
    </nav>
  );
}

7.5 Navigate / Redirect

The Navigate component is used to redirect users programmatically.

import { Navigate } from "react-router-dom";

function Profile({ isLoggedIn }) {
  return isLoggedIn ? <h1>Welcome User</h1> : <Navigate to="/login" />;
}

7.6 useNavigate Hook

The useNavigate hook lets you navigate programmatically inside functions.

import { useNavigate } from "react-router-dom";

function Home() {
  const navigate = useNavigate();

  return <button onClick={() => navigate("/about")}>Go to About</button>;
}

7.7 useParams Hook

useParams lets you access URL parameters.

import { useParams } from "react-router-dom";

function UserProfile() {
  const { id } = useParams();
  return <h2>User ID: {id}</h2>;
}

7.8 useLocation Hook

useLocation gives information about the current URL (pathname, search, state).

import { useLocation } from "react-router-dom";

function ShowLocation() {
  const location = useLocation();
  return <pre>{JSON.stringify(location, null, 2)}</pre>;
}

7.9 Outlet for Nested Routes

Outlet is used to render child routes inside a parent layout.

import { Outlet, Link } from "react-router-dom";

function Dashboard() {
  return (
    <div>
      <nav>
        <Link to="profile">Profile</Link>
        <Link to="settings">Settings</Link>
      </nav>
      <Outlet /> {/* Child routes will render here */}
    </div>
  );
}

7.10 Protected Routes

Protected routes restrict access based on authentication.

import { Navigate } from "react-router-dom";

function ProtectedRoute({ children, isAuthenticated }) {
  return isAuthenticated ? children : <Navigate to="/login" />;
}

Task: Create a protected dashboard route accessible only if logged in.


React Fragments

In React, Fragments allow you to group multiple elements without adding an extra DOM node (like a <div>). This is very useful when returning multiple components from a React component’s render method.

8.1 Introduction to Fragments

Normally in React, when you return multiple elements, they need to be wrapped inside a parent element. Fragments help avoid unnecessary HTML wrappers and keep the DOM clean.

function Example() {
  return (
    <React.Fragment>
      <h1>Hello</h1>
      <p>This is a paragraph.</p>
    </React.Fragment>
  );
}

Task: Create a component that displays a heading and a list of items using Fragments.

8.2 <React.Fragment> Syntax

The standard way of using fragments is by writing <React.Fragment></React.Fragment>. This ensures no extra <div> is added in the DOM.

function App() {
  return (
    <React.Fragment>
      <h2>Welcome</h2>
      <p>This content is inside a fragment.</p>
    </React.Fragment>
  );
}

8.3 Short Syntax <> </>

Instead of writing <React.Fragment>, React also supports a shorthand syntax: <> </>. This is cleaner and commonly used, but it doesn’t support keys or attributes.

function App() {
  return (
    <>
      <h2>Short Syntax</h2>
      <p>This uses <> and </>.</p>
    </>
  );
}

Task: Rewrite the previous fragment example using short syntax.

8.4 Keyed Fragments

When rendering lists, sometimes you need to provide a key. Short syntax <></> doesn’t allow keys, so you must use <React.Fragment key="...">.

const items = ["Apple", "Banana", "Mango"];

function ItemList() {
  return items.map((item, index) => (
    <React.Fragment key={index}>
      <h4>Item {index + 1}</h4>
      <p>{item}</p>
    </React.Fragment>
  ));
}

Task: Display a list of students with names and grades using keyed fragments.

8.5 Difference Between Div & Fragment

Using <div> introduces extra DOM nodes, which may cause unwanted styling or nesting. <React.Fragment> avoids this by grouping without rendering extra nodes.

// Using Div (adds extra div in DOM)
function DivWrapper() {
  return (
    <div>
      <h1>Title</h1>
      <p>Paragraph</p>
    </div>
  );
}

// Using Fragment (no extra div in DOM)
function FragmentWrapper() {
  return (
    <>
      <h1>Title</h1>
      <p>Paragraph</p>
    </>
  );
}

8.6 Use Cases of Fragments

  • Returning multiple elements from a component without extra nodes.
  • Rendering lists of elements without unnecessary wrappers.
  • Maintaining cleaner DOM structure for CSS/HTML frameworks.

Task: Build a card component that uses fragments to wrap title, description, and footer.

8.7 Limitations of Fragments

  • Shorthand syntax <></> cannot accept keys or attributes.
  • Cannot directly apply props like className or id to fragments.

Mini Project: Profile Card

Create a profile card component where the header, body, and footer are wrapped in a fragment instead of a div.

function ProfileCard() {
  return (
    <>
      <h2>John Doe</h2>
      <p>Software Developer</p>
      <footer>Contact: john@example.com</footer>
    </>
  );
}

Web Storage in JavaScript

The Web Storage API provides a way to store data in the browser. It offers two mechanisms: localStorage and sessionStorage. Unlike cookies, storage data is not sent to the server on every request, making it faster and more efficient.

9.1 Introduction to Web Storage API

Web Storage allows web applications to store key-value pairs in the browser. The data is stored as strings and can persist even after refreshing the page.

  • localStorage: Data persists even after the browser is closed.
  • sessionStorage: Data persists only until the browser tab is closed.

9.2 Difference: LocalStorage vs SessionStorage

Feature localStorage sessionStorage
Lifetime Persists until manually cleared Cleared when tab is closed
Storage Size ~5-10 MB ~5 MB
Scope Available across tabs and windows Restricted to the same tab

9.3 Storing Data

You can store data using setItem(key, value). Both key and value must be strings.

localStorage.setItem("username", "JohnDoe");
sessionStorage.setItem("theme", "dark");

Task: Store your favorite color in localStorage and theme preference in sessionStorage.

9.4 Retrieving Data

Use getItem(key) to retrieve stored values.

let user = localStorage.getItem("username");
let theme = sessionStorage.getItem("theme");

console.log(user);  // "JohnDoe"
console.log(theme); // "dark"

9.5 Removing Data

You can remove specific data using removeItem(key), or clear all data using clear().

localStorage.removeItem("username");
sessionStorage.clear();

9.6 Storing JSON Objects

Since storage only supports strings, objects must be converted to JSON using JSON.stringify() before storing, and parsed back using JSON.parse() when retrieving.

const user = {name: "Alice", age: 25};

localStorage.setItem("user", JSON.stringify(user));

let storedUser = JSON.parse(localStorage.getItem("user"));
console.log(storedUser.name); // "Alice"

9.7 Use Cases

  • Saving user preferences (dark mode, language).
  • Storing authentication tokens temporarily.
  • Remembering shopping cart items in e-commerce websites.
  • Saving form data temporarily.

9.8 Limitations

  • Data is stored as strings only.
  • Storage size is limited (~5MB).
  • Not suitable for sensitive data (accessible via JavaScript).
  • No automatic expiration like cookies.

Mini Project: Todo List with localStorage

Create a simple Todo List where tasks are stored in localStorage so they persist even after refreshing the page.

const tasks = JSON.parse(localStorage.getItem("tasks")) || [];

function addTask(task) {
  tasks.push(task);
  localStorage.setItem("tasks", JSON.stringify(tasks));
}

addTask("Learn Storage API");
console.log(JSON.parse(localStorage.getItem("tasks")));

Data Fetching in JavaScript

Data fetching is the process of requesting and retrieving data from a server or an API. JavaScript provides multiple ways to fetch data such as the Fetch API and third-party libraries like Axios. Understanding data fetching is essential for building interactive and dynamic web applications.

10.1 Introduction to Data Fetching

Data fetching allows us to communicate with servers to get or send data. For example, retrieving a list of users, submitting a form, updating records, or deleting items. The most common data format is JSON (JavaScript Object Notation).

10.2 Using Fetch API

The fetch() function is a modern, built-in JavaScript API for making network requests.

fetch("https://jsonplaceholder.typicode.com/posts")
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error("Error:", error));

Task: Use fetch() to get a list of users and log them to the console.

10.3 Using Axios

Axios is a popular third-party library that simplifies HTTP requests. It automatically handles JSON data and works in both browsers and Node.js.

axios.get("https://jsonplaceholder.typicode.com/posts")
  .then(response => console.log(response.data))
  .catch(error => console.error("Error:", error));

Task: Install Axios using npm install axios and fetch posts from an API.

10.4 Handling JSON Response

Most APIs return data in JSON format. With fetch(), you need to call response.json(), while Axios automatically parses JSON.

// Fetch API
fetch("https://jsonplaceholder.typicode.com/users")
  .then(response => response.json())
  .then(users => console.log(users));

// Axios
axios.get("https://jsonplaceholder.typicode.com/users")
  .then(res => console.log(res.data));

10.5 Error Handling

Errors may occur due to invalid URLs, network issues, or server problems. Use catch() with Promises or try...catch with async/await.

fetch("https://wrong-url.com")
  .then(response => {
      if (!response.ok) throw new Error("Network response was not ok");
      return response.json();
  })
  .catch(error => console.error("Fetch Error:", error));

10.6 Loading States

While fetching data, it’s important to show users that something is loading. In React, you can use state to track the loading process.

let loading = true;

fetch("https://jsonplaceholder.typicode.com/posts")
  .then(res => res.json())
  .then(data => {
      loading = false;
      console.log("Data Loaded:", data);
  });

if (loading) {
  console.log("Loading...");
}

10.7 Async/Await with Fetch & Axios

async/await makes asynchronous code look synchronous and easier to read.

// Using Fetch
async function getPosts() {
  try {
    const response = await fetch("https://jsonplaceholder.typicode.com/posts");
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error("Error:", error);
  }
}
getPosts();

// Using Axios
async function getUsers() {
  try {
    const res = await axios.get("https://jsonplaceholder.typicode.com/users");
    console.log(res.data);
  } catch (error) {
    console.error("Error:", error);
  }
}
getUsers();

10.8 HTTP Methods with Fetch & Axios

APIs often require different HTTP methods:

  • GET → Retrieve data
  • POST → Create new data
  • PUT → Update/replace existing data
  • PATCH → Partially update existing data
  • DELETE → Remove data
// ----------------------
// GET
fetch("https://jsonplaceholder.typicode.com/posts/1")
  .then(res => res.json())
  .then(data => console.log("GET:", data));

axios.get("https://jsonplaceholder.typicode.com/posts/1")
  .then(res => console.log("GET:", res.data));

// ----------------------
// POST
fetch("https://jsonplaceholder.typicode.com/posts", {
  method: "POST",
  headers: {"Content-Type": "application/json"},
  body: JSON.stringify({ title: "New Post", body: "Hello World", userId: 1 })
})
.then(res => res.json())
.then(data => console.log("POST:", data));

axios.post("https://jsonplaceholder.typicode.com/posts", {
  title: "New Post", body: "Hello World", userId: 1
})
.then(res => console.log("POST:", res.data));

// ----------------------
// PUT (replace whole resource)
fetch("https://jsonplaceholder.typicode.com/posts/1", {
  method: "PUT",
  headers: {"Content-Type": "application/json"},
  body: JSON.stringify({ id: 1, title: "Updated", body: "Updated Content", userId: 1 })
})
.then(res => res.json())
.then(data => console.log("PUT:", data));

axios.put("https://jsonplaceholder.typicode.com/posts/1", {
  id: 1, title: "Updated", body: "Updated Content", userId: 1
})
.then(res => console.log("PUT:", res.data));

// ----------------------
// PATCH (update only some fields)
fetch("https://jsonplaceholder.typicode.com/posts/1", {
  method: "PATCH",
  headers: {"Content-Type": "application/json"},
  body: JSON.stringify({ title: "Partially Updated Title" })
})
.then(res => res.json())
.then(data => console.log("PATCH:", data));

axios.patch("https://jsonplaceholder.typicode.com/posts/1", {
  title: "Partially Updated Title"
})
.then(res => console.log("PATCH:", res.data));

// ----------------------
// DELETE
fetch("https://jsonplaceholder.typicode.com/posts/1", { method: "DELETE" })
  .then(() => console.log("Deleted with Fetch"));

axios.delete("https://jsonplaceholder.typicode.com/posts/1")
  .then(() => console.log("Deleted with Axios"));

10.9 Best Practices

  • Always handle errors with try...catch or catch().
  • Show loading indicators for better user experience.
  • Use async/await for cleaner code.
  • Keep API URLs in a config file or environment variables.
  • Cache responses when possible to avoid unnecessary network calls.
  • Use Axios interceptors for authentication tokens and global error handling.

Mini Project: User List App

Create a small program that fetches user data from an API and displays names in the console. Handle loading states, errors, and use async/await for better readability.

async function fetchUsers() {
  console.log("Loading users...");
  try {
    const res = await fetch("https://jsonplaceholder.typicode.com/users");
    const users = await res.json();
    console.log("Users Loaded:");
    users.forEach(user => console.log(user.name));
  } catch (error) {
    console.error("Failed to fetch users:", error);
  }
}

fetchUsers();

React useContext Hook

The useContext hook in React provides a way to pass data through the component tree without manually passing props at every level (prop drilling). It makes your code cleaner and more efficient when multiple components need the same data.

11.1 Introduction to useContext

The useContext hook is used to consume values from a React Context. Context is designed to share data like theme, authentication, or language across the app. It works in combination with React.createContext and Context.Provider.

import React, { useContext, createContext } from "react";

const ThemeContext = createContext("light");

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar() {
  const theme = useContext(ThemeContext);
  return <h1>Current theme: {theme}</h1>;
}

Task: Create a context for "language" and display it inside a child component using useContext.

11.2 Creating Context

You can create context using React.createContext(). The context object comes with a Provider and Consumer.

import { createContext } from "react";

const UserContext = createContext(null);

export default UserContext;

Task: Create a UserContext that stores username and email.

11.3 Using Context.Provider

The Provider component makes data available to all child components. You wrap your component tree with the provider and set its value prop.

import UserContext from "./UserContext";

function App() {
  const user = { name: "Alice", email: "alice@example.com" };

  return (
    <UserContext.Provider value={user}>
      <Dashboard />
    </UserContext.Provider>
  );
}

Task: Wrap your app with UserContext.Provider and pass user details.

11.4 Consuming Context with useContext

The useContext hook allows you to consume context values directly. It avoids using the older Context.Consumer syntax, making the code shorter and cleaner.

import React, { useContext } from "react";
import UserContext from "./UserContext";

function Dashboard() {
  const user = useContext(UserContext);
  return <h2>Welcome, {user.name}! Your email is {user.email}</h2>;
}

Task: Consume the UserContext in multiple components.

11.5 Nested Contexts

You can use multiple contexts in the same application. For example, a ThemeContext for UI theme and a UserContext for user info.

const ThemeContext = createContext("light");
const UserContext = createContext({});

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <UserContext.Provider value={{ name: "Bob" }}>
        <Profile />
      </UserContext.Provider>
    </ThemeContext.Provider>
  );
}

function Profile() {
  const theme = useContext(ThemeContext);
  const user = useContext(UserContext);
  return <p>User: {user.name}, Theme: {theme}</p>;
}

Task: Combine ThemeContext and UserContext in one component.

11.6 Best Practices

  • Use context for global data like theme, auth, or settings.
  • Don’t overuse context; for local state, use useState.
  • Keep context values simple (avoid passing huge objects).
  • Split contexts if they handle unrelated data.
  • Combine useContext with custom hooks for cleaner code.

Mini Project: Theme Switcher

Build a small React app where you use useContext to manage dark/light themes. A button toggles between themes, and child components automatically update.

import React, { useState, useContext, createContext } from "react";

const ThemeContext = createContext();

function App() {
  const [theme, setTheme] = useState("light");

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <Header />
      <Content />
    </ThemeContext.Provider>
  );
}

function Header() {
  const { theme, setTheme } = useContext(ThemeContext);
  return (
    <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
      Toggle Theme
    </button>
  );
}

function Content() {
  const { theme } = useContext(ThemeContext);
  return <h1>Current Theme: {theme}</h1>;
}

export default App;

Redux

Redux is a predictable state container for JavaScript applications. It helps manage global state in large-scale apps, making data flow consistent and debugging easier. Redux is often used with React but works with any JavaScript framework.

12.1 Introduction to Redux

Redux provides a centralized store that holds the state of your application. Instead of passing props deeply, components can access the state directly from the store. The main idea is: Single Source of Truth.

// Install redux before using
// npm install redux react-redux

// Simple Redux store
import { createStore } from "redux";

function counterReducer(state = 0, action) {
  switch (action.type) {
    case "INCREMENT":
      return state + 1;
    case "DECREMENT":
      return state - 1;
    default:
      return state;
  }
}

const store = createStore(counterReducer);
console.log(store.getState()); // 0

Task: Create a simple Redux store with a counter reducer and test increment/decrement actions.

12.2 Core Concepts (Store, Actions, Reducers)

Redux is built on three core principles:

  • Store: Holds the entire state of the app.
  • Actions: Plain objects that describe "what happened".
  • Reducers: Functions that take the current state and action, then return a new state.
// Action
const increment = { type: "INCREMENT" };

// Reducer
function counterReducer(state = 0, action) {
  if (action.type === "INCREMENT") return state + 1;
  return state;
}

// Store
const store = createStore(counterReducer);
store.dispatch(increment);
console.log(store.getState()); // 1

Task: Write an action for "RESET" and handle it in the reducer.

12.3 Redux Workflow

Redux follows a unidirectional data flow:

  1. User interacts with the UI.
  2. An Action is dispatched.
  3. The Reducer updates the state based on the action.
  4. The Store notifies the UI of the updated state.
store.dispatch({ type: "INCREMENT" }); // Dispatch action
console.log(store.getState()); // Updated state

Task: Draw a diagram showing the Redux data flow (UI → Action → Reducer → Store → UI).

12.4 Redux with React

To use Redux with React, install react-redux and use Provider and hooks like useSelector and useDispatch.

import React from "react";
import { Provider, useSelector, useDispatch } from "react-redux";

function Counter() {
  const count = useSelector(state => state);
  const dispatch = useDispatch();

  return (
    <div>
      <h2>Count: {count}</h2>
      <button onClick={() => dispatch({ type: "INCREMENT" })}>+</button>
      <button onClick={() => dispatch({ type: "DECREMENT" })}>-</button>
    </div>
  );
}

function App({ store }) {
  return (
    <Provider store={store}>
      <Counter />
    </Provider>
  );
}

Task: Build a React counter app using Redux.

12.5 Redux Toolkit (RTK)

Redux Toolkit simplifies Redux setup with less boilerplate. It provides configureStore and createSlice.

import { configureStore, createSlice } from "@reduxjs/toolkit";

const counterSlice = createSlice({
  name: "counter",
  initialState: 0,
  reducers: {
    increment: (state) => state + 1,
    decrement: (state) => state - 1,
  },
});

export const { increment, decrement } = counterSlice.actions;
const store = configureStore({ reducer: counterSlice.reducer });

export default store;

Task: Rewrite your counter app using Redux Toolkit.

12.6 Middleware (Redux Thunk, Saga)

Middleware extends Redux to handle async logic like API calls. Common middleware:

  • Redux Thunk: Handles async functions inside actions.
  • Redux Saga: Uses generator functions for side effects.
// Example with Redux Thunk
import { configureStore, createSlice } from "@reduxjs/toolkit";
import thunk from "redux-thunk";

const dataSlice = createSlice({
  name: "data",
  initialState: [],
  reducers: {
    setData: (state, action) => action.payload,
  },
});

export const { setData } = dataSlice.actions;

const fetchData = () => async (dispatch) => {
  const response = await fetch("https://jsonplaceholder.typicode.com/posts");
  const data = await response.json();
  dispatch(setData(data));
};

const store = configureStore({
  reducer: dataSlice.reducer,
  middleware: [thunk],
});

export default store;

Task: Use Redux Thunk to fetch data and display it in a component.

12.7 Best Practices

  • Use Redux only when state is shared across many components.
  • Prefer Redux Toolkit over manual setup.
  • Keep reducers pure and avoid side effects inside them.
  • Use middleware for async tasks (API calls).
  • Break large slices into smaller ones for better maintainability.

Mini Project: Todo App with Redux

Create a Todo App that:

  • Uses Redux Toolkit for state management
  • Adds and removes todos
  • Marks todos as completed
  • Stores todos in a global store
import { createSlice, configureStore } from "@reduxjs/toolkit";
import { Provider, useSelector, useDispatch } from "react-redux";

const todoSlice = createSlice({
  name: "todos",
  initialState: [],
  reducers: {
    addTodo: (state, action) => [...state, { text: action.payload, completed: false }],
    toggleTodo: (state, action) => 
      state.map((todo, i) => i === action.payload ? { ...todo, completed: !todo.completed } : todo),
    removeTodo: (state, action) => state.filter((_, i) => i !== action.payload),
  },
});

const { addTodo, toggleTodo, removeTodo } = todoSlice.actions;
const store = configureStore({ reducer: todoSlice.reducer });

function TodoApp() {
  const todos = useSelector(state => state);
  const dispatch = useDispatch();
  let input;

  return (
    <div>
      <input ref={(node) => (input = node)} />
      <button onClick={() => { dispatch(addTodo(input.value)); input.value=""; }}>Add</button>
      <ul>
        {todos.map((todo, i) => (
          <li key={i} style={{ textDecoration: todo.completed ? "line-through" : "none" }}>
            {todo.text}
            <button onClick={() => dispatch(toggleTodo(i))}>Toggle</button>
            <button onClick={() => dispatch(removeTodo(i))}>Remove</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

function App() {
  return (
    <Provider store={store}>
      <TodoApp />
    </Provider>
  );
}

export default App;

React Class Components

Class Components in React are ES6 classes that extend from React.Component. They allow you to use state, lifecycle methods, and render() to return UI. Although Functional Components with hooks are now more common, Class Components are still widely used in legacy and large projects.

13.1 Introduction to Class Components

A class component must extend React.Component and include a render() method that returns JSX.

import React, { Component } from "react";

class Welcome extends Component {
    render() {
        return <h1>Hello, World from Class Component!</h1>;
    }
}

export default Welcome;
    

Task: Create a class component named Greeting that displays "Welcome to React!".

13.2 Rendering UI with render()

Every class component must have a render() method which returns JSX. This is how the UI is displayed.

class Message extends Component {
    render() {
        return (
            <div>
                <h2>This is rendered from a Class Component</h2>
            </div>
        );
    }
}
    

Task: Create a Profile class component that renders your name and age inside a <div>.

13.3 Constructor & State

State in class components is usually initialized in the constructor() method. this.state holds component data, and it can be updated using this.setState().

class Counter extends Component {
    constructor(props) {
        super(props);
        this.state = { count: 0 };
    }

    render() {
        return (
            <h2>Count: {this.state.count}</h2>
        );
    }
}
    

Task: Create a Counter class component with initial count = 5.

13.4 Props in Class Components

Props are passed from a parent component and accessed using this.props.

class Welcome extends Component {
    render() {
        return <h1>Hello, {this.props.name}!</h1>;
    }
}

// Usage
<Welcome name="Alice" />
    

Task: Create a class component User that accepts name and age as props and displays them.

13.5 Event Handling

Events in class components are handled using methods, and you must bind this if not using arrow functions.

class ButtonClick extends Component {
    constructor(props) {
        super(props);
        this.state = { message: "Not Clicked" };
        this.handleClick = this.handleClick.bind(this);
    }

    handleClick() {
        this.setState({ message: "Button Clicked!" });
    }

    render() {
        return (
            <div>
                <p>{this.state.message}</p>
                <button onClick={this.handleClick}>Click Me</button>
            </div>
        );
    }
}
    

Task: Create a button that updates text when clicked.

13.6 Lifecycle Methods

Lifecycle methods are special methods in Class Components:

  • componentDidMount() – Runs after the component mounts (good for API calls).
  • componentDidUpdate() – Runs after state/props update.
  • componentWillUnmount() – Runs before component is removed (cleanup).
class LifecycleDemo extends Component {
    componentDidMount() {
        console.log("Component Mounted");
    }

    componentDidUpdate() {
        console.log("Component Updated");
    }

    componentWillUnmount() {
        console.log("Component Unmounted");
    }

    render() {
        return <h2>Lifecycle Demo</h2>;
    }
}
    

13.7 Updating State

State must always be updated using this.setState() to trigger a re-render.

class Counter extends Component {
    constructor(props) {
        super(props);
        this.state = { count: 0 };
    }

    increment = () => {
        this.setState({ count: this.state.count + 1 });
    }

    render() {
        return (
            <div>
                <h2>Count: {this.state.count}</h2>
                <button onClick={this.increment}>+1</button>
            </div>
        );
    }
}
    

13.8 Conditional Rendering

You can conditionally render elements based on state or props.

class LoginStatus extends Component {
    constructor(props) {
        super(props);
        this.state = { isLoggedIn: true };
    }

    render() {
        return this.state.isLoggedIn ? 
            <h2>Welcome Back!</h2> : 
            <h2>Please Login</h2>;
    }
}
    

13.9 Class vs Function Components

Class Components:

  • Use this.state and this.setState
  • Have lifecycle methods
  • More verbose
Function Components (with Hooks):
  • Use useState, useEffect, and other hooks
  • Shorter and easier to write
  • Modern React standard

Mini Project: Class-based Todo App

Create a Todo App with Class Components that:

  • Stores todos in state
  • Adds new todos
  • Deletes todos
  • Conditionally renders "No todos left" if empty
class TodoApp extends Component {
    constructor(props) {
        super(props);
        this.state = { todos: [], input: "" };
    }

    handleChange = (e) => {
        this.setState({ input: e.target.value });
    }

    addTodo = () => {
        this.setState({
            todos: [...this.state.todos, this.state.input],
            input: ""
        });
    }

    render() {
        return (
            <div>
                <h2>Todo List</h2>
                <input value={this.state.input} onChange={this.handleChange} />
                <button onClick={this.addTodo}>Add</button>
                <ul>
                    {this.state.todos.length === 0 
                        ? <li>No todos left</li> 
                        : this.state.todos.map((todo, i) => <li key={i}>{todo}</li>)}
                </ul>
            </div>
        );
    }
}
    

Advanced Topics

In modern React and frontend development, advanced UI/UX patterns help improve performance, scalability, and user experience. Here, we will explore some key concepts such as toast notifications, infinite scroll, loaders, pagination, modals, file uploads, performance optimizations, and error boundaries.

14.1 Toast Notifications

Toast notifications are small, temporary pop-ups that show success, error, or informational messages. Libraries like react-toastify make it very easy to implement.

import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

function App() {
    const notify = () => toast.success("Data saved successfully!");
    return (
        <div>
            <button onClick={notify}>Show Toast</button>
            <ToastContainer />
        </div>
    );
}
    

Task: Create success and error toasts for form submission.

14.2 Infinite Scroll

Infinite scrolling loads content as the user scrolls, instead of pagination. This is useful for feeds (like Instagram, Twitter).

import { useState, useEffect } from "react";

function InfiniteScroll() {
    const [items, setItems] = useState(Array.from({ length: 20 }));

    const loadMore = () => {
        setItems(prev => [...prev, ...Array.from({ length: 10 })]);
    };

    useEffect(() => {
        const handleScroll = () => {
            if (window.innerHeight + window.scrollY >= document.body.offsetHeight) {
                loadMore();
            }
        };
        window.addEventListener("scroll", handleScroll);
        return () => window.removeEventListener("scroll", handleScroll);
    }, []);

    return <ul>{items.map((_, i) => <li key={i}>Item {i+1}</li>)}</ul>;
}
    

Task: Implement infinite scroll to load 10 items at a time.

14.3 Loading Bar / Spinner

Loaders/spinners indicate progress while data is being fetched or processed.

function Loader() {
    return <div className="spinner">Loading...</div>;
}
    

Task: Display a spinner while fetching API data.

14.4 Pagination

Pagination divides large data sets into smaller pages for performance and clarity.

function Pagination({ totalPages, currentPage, onPageChange }) {
    return (
        <div>
            {Array.from({ length: totalPages }).map((_, i) => (
                <button 
                  key={i} 
                  onClick={() => onPageChange(i+1)}
                  disabled={currentPage === i+1}
                >{i+1}</button>
            ))}
        </div>
    );
}
    

Task: Implement pagination for a student list.

14.5 Modal & Dialog Handling

Modals are overlays that display information or forms on top of the main UI.

function Modal({ open, onClose }) {
    if (!open) return null;
    return (
        <div className="overlay">
            <div className="modal">
                <h2>Modal Content</h2>
                <button onClick={onClose}>Close</button>
            </div>
        </div>
    );
}
    

Task: Create a modal that opens on button click and closes when clicked outside.

14.6 File Upload with Progress

File uploads can display progress bars for better UX. Libraries like Axios support tracking progress.

import axios from "axios";

function uploadFile(file) {
    const formData = new FormData();
    formData.append("file", file);

    axios.post("/upload", formData, {
        onUploadProgress: (progressEvent) => {
            let percent = Math.round((progressEvent.loaded * 100) / progressEvent.total);
            console.log(percent + "% uploaded");
        }
    });
}
    

Task: Upload a file and show progress percentage.

14.7 Debouncing & Throttling

These are performance techniques for event handling. Debounce: Trigger action after user stops typing.
Throttle: Limit action execution within a fixed interval.

// Debounce Example
function debounce(fn, delay) {
    let timer;
    return function(...args) {
        clearTimeout(timer);
        timer = setTimeout(() => fn.apply(this, args), delay);
    };
}

// Throttle Example
function throttle(fn, limit) {
    let inThrottle;
    return function(...args) {
        if (!inThrottle) {
            fn.apply(this, args);
            inThrottle = true;
            setTimeout(() => inThrottle = false, limit);
        }
    };
}
    

Task: Implement debounce for search input and throttle for window resize.

14.8 Lazy Loading & Code Splitting

Lazy loading loads components only when needed. Code splitting reduces bundle size.

import React, { Suspense, lazy } from "react";

const LazyComponent = lazy(() => import("./HeavyComponent"));

function App() {
    return (
        <Suspense fallback={<div>Loading...</div>}>
            <LazyComponent />
        </Suspense>
    );
}
    

Task: Lazy load a heavy chart component in React.

14.9 Error Boundaries

Error Boundaries catch JavaScript errors in components and prevent breaking the whole app. Only available in class components.

class ErrorBoundary extends React.Component {
    constructor(props) {
        super(props);
        this.state = { hasError: false };
    }

    static getDerivedStateFromError() {
        return { hasError: true };
    }

    componentDidCatch(error, errorInfo) {
        console.error("Error caught:", error, errorInfo);
    }

    render() {
        if (this.state.hasError) {
            return <h2>Something went wrong.</h2>;
        }
        return this.props.children;
    }
}
    

Task: Wrap a component with ErrorBoundary and trigger an error.

Mini Project: Social Media Feed

Build a mini social media feed with the following:

  • Use Toast to show new post success.
  • Load posts with Infinite Scroll.
  • Show Spinner while fetching.
  • Paginate comments with Pagination.
  • Open post details in a Modal.
  • Allow File Upload with progress for profile pictures.
  • Use Debounce for search bar.
  • Lazy Load heavy analytics component.
  • Wrap app with Error Boundary.

Deployment

Once your React or JavaScript project is ready, the next step is deployment. Deployment means taking your local project and making it available online so that others can use it. There are different platforms to deploy your apps like GitHub Pages, Netlify, Vercel, and Firebase Hosting.

15.1 Build Process (npm run build)

Before deploying, you must build your React project. The build process optimizes your app for production.

npm run build
    

This creates a build folder containing all optimized HTML, CSS, and JavaScript files.

Task: Run npm run build in your project and check the build folder.

15.2 Deploy on GitHub Pages

GitHub Pages is a free hosting service for static websites. You can easily deploy your React app.

  1. Install the package: npm install gh-pages --save-dev
  2. Add the following to package.json:
    "homepage": "https://username.github.io/repository-name"
  3. Add scripts:
    "predeploy": "npm run build",
    "deploy": "gh-pages -d build"
  4. Run npm run deploy

Your app will be live at your GitHub Pages URL.

15.3 Deploy on Netlify

Netlify provides free hosting with continuous deployment from GitHub.

  1. Create an account on Netlify
  2. Connect your GitHub repository
  3. Netlify will auto-detect React and set npm run build as the build command
  4. Deploy and get your app live at https://your-app-name.netlify.app

15.4 Deploy on Vercel

Vercel is another popular platform for React and Next.js apps.

  1. Install Vercel CLI (optional): npm i -g vercel
  2. Create an account at Vercel
  3. Import your GitHub repo
  4. Vercel will detect React and deploy automatically
  5. Your app will be live at https://your-app-name.vercel.app

15.5 Deploy on Firebase Hosting

Firebase Hosting by Google provides fast and secure static hosting.

  1. Install Firebase CLI: npm install -g firebase-tools
  2. Login: firebase login
  3. Initialize project: firebase init
  4. Choose Hosting and set build as the public directory
  5. Deploy: firebase deploy

Your app will be live at https://your-project-id.web.app.

Mini Project: Deploy Your App

Take a simple React project (like a Todo app), run npm run build, and deploy it on one of the platforms above. Share the live link with your friends!


React Tasks

Practice makes you a pro in React! Below are different React tasks and mini-projects that will help you understand JSX, props, state, hooks, routing, and state management step by step. Each task builds on the previous concepts.

16.1 JSX Tasks

JSX allows you to write HTML-like syntax inside JavaScript. Tasks:

  • Create a React component that displays a heading and a paragraph using JSX.
  • Use JavaScript expressions inside JSX (like showing current year).
function App() {
  const year = new Date().getFullYear();
  return (
    <div>
      <h1>Hello JSX</h1>
      <p>Year: {year}</p>
    </div>
  );
}

16.2 Props & Components Task

Props are used to pass data from parent to child components.

  • Create a Card component that takes title and description as props.
  • Render multiple Card components in App.
function Card({ title, description }) {
  return <div><h2>{title}</h2><p>{description}</p></div>;
}

16.3 State & useState Hook Tasks

The useState hook lets you manage component state.

  • Create a counter app with increment and decrement buttons.
  • Create a toggle button that shows/hides text.
const [count, setCount] = useState(0);

16.4 Conditional Rendering Task

Show or hide elements based on conditions.

  • Create a login/logout button that switches text based on state.
{isLoggedIn ? <p>Welcome!</p> : <p>Please log in</p>}

16.5 List Rendering Tasks

Render arrays dynamically in React.

  • Display a list of fruits using map().
{fruits.map(fruit => <li key={fruit}>{fruit}</li>)}

16.6 Form Handling Tasks

Manage input fields and form submissions.

  • Create a form with name and email input fields.
  • Show the entered values on submit.

16.7 useEffect Hook Tasks

The useEffect hook handles side effects like fetching data or timers.

  • Log a message when the component mounts.
  • Fetch data from an API and display it.

16.8 Context API Tasks

Context API helps share data without prop drilling.

  • Create a theme context with light/dark mode.
  • Toggle the theme from a button.

16.9 useReducer Hook Tasks

useReducer is great for managing complex state.

  • Build a counter with useReducer.
  • Manage a todo list with useReducer.

16.10 Custom Hooks Tasks

Create reusable logic with custom hooks.

  • Build a useLocalStorage hook.
  • Build a useFetch hook.

16.11 React Router Basics Tasks

React Router allows navigation between pages.

  • Create routes: Home, About, and Contact.
  • Use Link to navigate between pages.

16.12 React Router Advanced Tasks

Advanced concepts include nested routes and protected routes.

  • Create a dashboard with nested routes.
  • Protect a route so only logged-in users can access it.

16.13 Redux / Zustand State Management Tasks

For larger apps, use Redux or Zustand for global state management.

  • Create a global counter with Redux.
  • Build a store with Zustand for managing todos.

16.14 Toast Notifications Tasks

Use libraries like react-toastify to show notifications.

  • Show a toast message when a form is submitted.

16.15 Pagination Tasks

Split data into pages.

  • Fetch 100 items and show 10 per page with next/prev buttons.

16.16 Search & Filter List Tasks

Implement search and filtering.

  • Create a search bar that filters a list of names.

16.17 Loading Spinner / Progress Bar Tasks

Show loading states for better UX.

  • Show a spinner while fetching data from API.

16.18 Infinite Scroll Project

Build an infinite scrolling list using React and API fetching.

16.19 Weather App Project

Use an open weather API to build a weather forecast app.

16.20 Movie Search App Project

Fetch movies from OMDB API and display search results.

16.21 Todo App Project

Classic React project to practice CRUD operations.

16.22 Expense Tracker Project

Build an expense tracker with income/expense charts.

16.23 Tic Tac Toe Game Project

Build a two-player Tic Tac Toe game in React.

16.24 Rock-Paper-Scissors Game Project

Create a fun game where the user plays against the computer.