Unit 2 Fundamentals of React
Unit 2 Fundamentals of React
JS
React is an open source, JavaScript library for developing user interface (UI) in web application. React
is developed and released by Facebook. Facebook is continuously working on the React library and
enhancing it by fixing bugs and introducing new features.
ReactJS is a simple, feature rich, component based JavaScript UI library. It can be used to develop small
applications as well as big, complex applications. ReactJS provides minimal and solid feature set to
kick-start a web application. React community compliments React library by providing large set of
ready-made components to develop web application in a record time. React community also provides
advanced concept like state management, routing, etc., on top of the React library.
Features:
The salient features of React library are as follows −
• Solid base architecture
• Extensible architecture
• Component-based library
• JSX-based design architecture
• Declarative UI library
Benefits :
MS. ESHA VINAY PATEL 1
UNIT 2 : FUNDAMENTALS OF REACT.JS
Applications :
Few popular websites powered by React library are listed below –
JSX (JavaScript XML): JSX is a syntax extension that allows HTML-like code within
JavaScript for readability. React transforms JSX into JavaScript calls that create and
manage DOM elements.
Virtual DOM: React uses a Virtual DOM, a lightweight copy of the real DOM, to
optimize rendering. Updates are first applied to the Virtual DOM. React efficiently
calculates and updates only the necessary parts of the actual DOM, improving
performance.
Props: Props (properties) are read-only inputs passed from parent to child components
for data flow and reusability. This illustrates one-way data flow from parent to child.
State: State is a mutable JavaScript object within a component that allows it to manage
and update its data. State updates trigger re-renders of the element.
Lifecycle Methods (for Class Components): Class components have a lifecycle with
phases (Mounting, Updating, and Unmounting) where specific methods can be used to
perform actions.
Hooks (for Functional Components): Introduced in React 16.8, Hooks allow functional
components to use state ( useState ) and side effects ( useEffect ) without being class
components, improving code readability.
xml
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>React with HTML Example</title>
<!-- React and ReactDOM libraries from CDN -->
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<!-- Babel for JSX support -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<!-- Container for React -->
<div id="root"></div>
<script type="text/babel">
// React component returning JSX (like HTML)
function App() {
return (
<div>
<h1>Hello from React!</h1>
<p>This paragraph is rendered by React inside an HTML page.</p>
</div>
);
}
Prerequisites
Install Node.js (version 14 or higher) from nodejs.org.
Node includes npm (node package manager) which you use to run commands.
bash
npx create-react-app my-app
o npx lets you run the CRA tool without installing it globally.
o my-app is your project folder name (you can change it).
3. Navigate into the project directory:
bash
cd my-app
bash
npm start
What Happens?
CRA creates a folder with your project and installs dependencies.
You get a working React application with a main component in src/App.js.
You can modify src/App.js to change your app.
Example src/App.js:
jsx
import React from 'react';
function App() {
return (
<div>
<h1>Hello, React App!</h1>
<p>Welcome to your new React project.</p>
</div>
);
}
bash
npx create-react-app my-app
cd my-app
npm start
This will start the development server and open your app in the browser.
jsx
import React from 'react';
function FunctionalComponent() {
return (
<div>
<h1>Hello World from Functional Component!</h1>
<p>This is a functional React component.</p>
</div>
);
}
jsx
import React, { Component } from 'react';
jsx
import React from 'react';
import FunctionalComponent from './FunctionalComponent';
import ClassComponent from './ClassComponent';
function App() {
return (
<div>
<FunctionalComponent />
<ClassComponent />
</div>
);
}
Summary
Component Type Syntax Notes
Functional Simpler syntax, recommended in
Function returning JSX
Component modern React
Class extending React.Component with Older style, supports state &
Class Component
render() method lifecycle
The parent component renders one or more child components inside its JSX.
Child components can themselves contain further nested components, enabling
deep, hierarchical UI structures.
React’s design encourages composition (combining components) rather than
inheritance.
jsx
import React from 'react';
function Child() {
return <div>This is the Child Component</div>;
}
jsx
import React from 'react';
import Child from './Child'; // Import child component
function Parent() {
return (
<div>
<h2>Parent Component</h2>
<Child /> {/* Use (render) Child inside Parent */}
</div>
);
}
jsx
import React from 'react';
import Parent from './Parent';
function App() {
return (
<div>
<h1>Nested Components Example</h1>
<Parent />
</div>
);
}
In this setup:
In Parent.js:
jsx
function Parent() {
const message = "Hello from Parent!";
return (
<div>
<Child greeting={message} />
</div>
);
}
In Child.js:
jsx
function Child(props) {
return <div>Message from parent: {props.greeting}</div>;
}
This demonstrates:
ListParent.js:
jsx
import React from 'react';
import ListItem from './ListItem';
function ListParent() {
return (
<ul>
{fruits.map((fruit, index) => (
<ListItem key={index} name={fruit} />
))}
</ul>
);
}
ListItem.js:
jsx
import React from 'react';
Use key props on list items to help React track item identity for efficient updates.
Child components receive relevant data via props dynamically.
text
/src
/components
Child.js
Parent.js
ListItem.js
ListParent.js
App.js
index.js
jsx
function MenuItem({ item }) {
return (
<li>
{item.label}
{item.children && item.children.length > 0 && (
<ul>
{item.children.map((child) => (
<MenuItem key={child.label} item={child} />
))}
</ul>
)}
</li>
);
}
jsx
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click Me</button>
</div>
);
}
jsx
function Parent() {
return (
<div>
<Counter />
<Counter />
</div>
);
}
function ChildFunctional(props) {
return (
<div>
<h3>Child Functional Component</h3>
<p>Greeting: {props.greeting}</p>
<p>Number: {props.number}</p>
<button onClick={props.onButtonClick}>Click Me (Functional)</button>
</div>
);
}
render() {
return (
<div>
<h3>Child Class Component</h3>
<p>Greeting: {this.props.greeting}</p>
<p>Number: {this.props.number}</p>
<button onClick={this.props.onButtonClick}>Click Me (Class)</button>
</div>
);
}
}
function Parent() {
const greetMessage = "Hello from Parent!";
const numberValue = 123;
return (
<div>
<h2>Parent Component</h2>
number={numberValue}
onButtonClick={handleButtonClick}
/>
</div>
);
}
function App() {
return (
<div className="App" style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
<h1>React Props Passing Example</h1>
<Parent />
</div>
);
}
bash
npx create-react-app my-app
cd my-app
2. Inside src/, create these files:
App.js (replace the existing one with the above code)
Parent.js (add the parent component code)
ChildFunctional.js (add functional child code)
ChildClass.js (add class child code)
3. Start the app in development mode:
bash
npm start
4. Open http://localhost:3000 in your browser to see the result:
You will see the parent and both children rendered.
Clicking either child's button will trigger an alert showing the passed
callback function is working correctly.
Pass functions as
props <Child onButtonClick={func} /> <Child onButtonClick={func} />
Invoke callback in
child onClick={props.onButtonClick} onClick={this.props.onButtonClick}
Here’s a detailed overview of React class components along with key features, syntax,
differences from functional components, and examples:
// Initialize state
this.state = {
count: 0,
};
// Event handler
handleClick() {
this.setState({ count: this.state.count + 1 });
}
Key Points:
constructor(props) initializes state and binds methods (necessary in older React or non-
arrow functions).
render() returns the JSX UI.
state is an object holding the component’s internal, mutable data.
this.setState() updates state and triggers re-render.
Lifecycle methods let you perform actions when the component mounts, updates, or
unmounts.
Lifecycle Hooks to run code at key stages: mounting, updating, unmounting. E.g.,
Methods componentDidMount(), componentDidUpdate(), componentWillUnmount().
Event
Methods written as class functions, need binding if not arrow functions.
Handling
Props Access Via this.props, read-only data passed from parent.
Render
A mandatory method returning JSX for UI display.
Method
increment = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<h2>{this.props.title}</h2>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
jsx
import React from 'react';
import HelloWorldClass from './HelloWorldClass';
import Counter from './Counter';
function App() {
return (
<div>
<HelloWorldClass />
<Counter title="Counter Example" />
</div>
);
}
Summary
Class components are ES6 classes extending React.Component.
They must include a render method that returns JSX.
Support state and lifecycle methods inherently.
Use this.props for props and this.state for internal mutable data.
Often more verbose but important for understanding React’s foundational model.
Functional components with hooks have largely superseded class components in
modern React.
if (isLoggedIn) {
return <h1>Welcome back!</h1>;
} else {
return <h1>Please sign up.</h1>;
}
}
Example:
jsx
function Greeting(props) {
return (
<h1>
{props.isLoggedIn ? "Welcome back!" : "Please sign up."}
</h1>
);
}
Example:
jsx
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
}
Examples:
Arithmetic:
jsx
const total = props.price * props.quantity;
Logical:
jsx
{isActive && <button>Deactivate</button>}
Ternary inline:
jsx
{isActive ? "Active" : "Inactive"}
);
return (
<ul>{listItems}</ul>
);
}
Usage:
jsx
const numbers = [1, 2, 3, 4, 5];
<NumberList numbers={numbers} />
function TaskList(props) {
const { tasks, showCompleted } = props;
return (
<div>
<h2>Tasks</h2>
<ul>
{tasks
.filter(task => showCompleted || !task.completed)
.map(task => (
<li key={task.id}>
return (
<div>
{isLoggedIn ? (
<h1>Welcome, {username}!</h1>
):(
<h1>Please log in.</h1>
)}
</div>
);
}
}
Logical AND
Render only if condition true {hasNotifications && <Notification />}
(&&)
render() {
return <button onClick={this.handleClick}>Click Me</button>;
}
}
jsx
function GreetButton(props) {
function handleGreet(name) {
alert("Hello, " + name);
}
return (
<button onClick={() => handleGreet(props.name)}>
Greet {props.name}
</button>
);
}
render() {
return (
<button onClick={() => this.handleGreet(this.props.name)}>
Greet {this.props.name}
</button>
);
}
}
It wraps the browser’s native event and has the same interface (for
cross-browser compatibility).
render() {
return <input onChange={this.handleInput} />;
}
}
function EventDemo() {
const [count, setCount] = useState(0);
return (
<div>
<h3>Count: {count}</h3>
<button onClick={(e) => handleClick(e, 1)}>Increase</button>
<button onClick={(e) => handleClick(e, -1)}>Decrease</button>
</div>
);
}
Explanation:
5. Summary Table
Task Functional Component Class Component
Add event <button onClick={handler}> <button onClick={this.handler}>
Pass argument onClick={() => handler(arg)} onClick={() => this.handler(arg)}
Access event object handler(event) handler(event)
Access event target value event.target.value event.target.value
return (
<form onSubmit={handleSubmit}>
<button type="submit">Submit</button>
</form>
);
}
jsx
import React, { useState } from 'react';
function SimpleForm() {
const [name, setName] = useState('');
function handleChange(event) {
setName(event.target.value);
}
function handleSubmit(event) {
event.preventDefault();
alert(`Submitted name: ${name}`);
}
return (
<form onSubmit={handleSubmit}>
<input type="text" value={name} onChange={handleChange} />
<button type="submit">Submit</button>
</form>
);
}
jsx
import React, { useState } from 'react';
function MultiFieldForm() {
const [inputs, setInputs] = useState({ username: '', age: '' });
function handleChange(event) {
const { name, value } = event.target;
setInputs(inputs => ({ ...inputs, [name]: value }));
}
function handleSubmit(event) {
event.preventDefault();
alert(`Username: ${inputs.username}, Age: ${inputs.age}`);
}
return (
<form onSubmit={handleSubmit}>
<input type="text" name="username" value={inputs.username} onChange={handleChange} />
<input type="number" name="age" value={inputs.age} onChange={handleChange} />
<button type="submit">Submit</button>
</form>
);
}
Setting the name attribute allows the generic handleChange to update the correct field.
React.memo
React.memo is a higher-order component to optimize functional components—it
prevents unnecessary re-renders by memoizing the component output, only
updating when the props change.
It’s useful in large forms with many child components that don’t always need to re-
render.
Example:
jsx
const OptimizedInput = React.memo(function OptimizedInput(props) {
return <input {...props} />;
});
jsx
<textarea
name="message"
value={inputs.message}
onChange={handleChange}
/>
jsx
<select name="flavor" value={inputs.flavor} onChange={handleChange}>
<option value="apple">Apple</option>
<option value="banana">Banana</option>
<option value="cherry">Cherry</option>
</select>
jsx
<select name="fruits" multiple value={inputs.fruits} onChange={handleChange}>
<option value="apple">Apple</option>
<option value="banana">Banana</option>
<option value="cherry">Cherry</option>
</select>
jsx
function FullForm() {
const [inputs, setInputs] = useState({
name: '',
comment: '',
color: '',
});
function handleChange(event) {
const { name, value } = event.target;
setInputs(inputs => ({ ...inputs, [name]: value }));
}
function handleSubmit(event) {
event.preventDefault();
alert(JSON.stringify(inputs));
}
return (
<form onSubmit={handleSubmit}>
<input type="text" name="name" value={inputs.name} onChange={handleChange} />
<textarea name="comment" value={inputs.comment} onChange={handleChange} />
<select name="color" value={inputs.color} onChange={handleChange}>
<option value="">Pick a color</option>
<option value="red">Red</option>
<option value="blue">Blue</option>
</select>
<button type="submit">Submit</button>
</form>
);
}
Shows text input, textarea, and dropdown working together; all handled via name,
value, and one function.
Summary Table
Feature How to Use Example Code
Single Input Set value, use onChange/setState <input value={state} onChange={handler} />
Use name + event.target.name in
Multiple Fields <input name="x" onChange={handler} />
handler
Same as input, with value and
Textarea <textarea value={state} onChange={handler} />
onChange
Dropdown Controlled value, update <select value={state}
(Select) onChange onChange={handler}>...</select>
React.memo Optimize function components const Comp = React.memo(MyComp)
Identify input for handler, update
event.target.name const {name, value} = event.target
by name
return newErrors;
};
setErrors(validationErrors);
if (Object.keys(validationErrors).length === 0) {
alert("Registration successful!");
console.log(formData);
}
};
return (
<form onSubmit={handleSubmit} style={{ maxWidth: "400px", margin: "auto" }}>
<h2>Registration Form</h2>
<input
type="text"
name="fullName"
placeholder="Full Name"
value={formData.fullName}
onChange={handleChange}
/>
<div style={{ color: "red" }}>{errors.fullName}</div>
<input
type="email"
name="email"
placeholder="Email"
value={formData.email}
onChange={handleChange}
/>
<div style={{ color: "red" }}>{errors.email}</div>
<input
type="password"
name="password"
placeholder="Password"
value={formData.password}
onChange={handleChange}
/>
<div style={{ color: "red" }}>{errors.password}</div>
<input
type="password"
name="confirmPassword"
placeholder="Confirm Password"
value={formData.confirmPassword}
onChange={handleChange}
/>
<div style={{ color: "red" }}>{errors.confirmPassword}</div>
<input
type="text"
name="phone"
placeholder="Phone Number"
value={formData.phone}
onChange={handleChange}
/>
<div style={{ color: "red" }}>{errors.phone}</div>
<div>
Gender:
<label>
<input
type="radio"
name="gender"
value="male"
checked={formData.gender === "male"}
onChange={handleChange}
/>
Male
</label>
<label>
<input
type="radio"
name="gender"
value="female"
checked={formData.gender === "female"}
onChange={handleChange}
/>
Female
</label>
</div>
<div style={{ color: "red" }}>{errors.gender}</div>
<label>
<input
type="checkbox"
name="terms"
checked={formData.terms}
onChange={handleChange}
/>
I accept the Terms & Conditions
</label>
<div style={{ color: "red" }}>{errors.terms}</div>
<button type="submit">Register</button>
</form>
);
};
2. app.js
function App() {
return (
<div className="App">
<RegistrationForm />
</div>
);
}
A hook in React is a special function that lets you "hook into" React features—such as state,
lifecycle, or context—directly from function components, without needing to write class
components.
Key Examples
useState: Enables a function component to have its own state.
useEffect: Handles side effects (like data fetching or subscriptions) in function
components, replacing methods like componentDidMount in classes.
Custom Hooks: Lets you extract and share logic between components.
Summary Table
Feature With Hooks Without Hooks
What is useState?
useStateis a React Hook that lets you add stateful logic to functional components.
Before Hooks, only class components could have state; useState enables function
components to maintain and update state.
It provides a pair of values:
o The current value of the state.
Example:
jsx
const [count, setCount] = useState(0);
Important Details
Calling setCount does not immediately update the state variable in the current render
or synchronous code; the update will be reflected on the next render.
useState can hold any data type:
o Primitive types like number, string, boolean.
o Complex types like arrays, objects.
You can call useState multiple times in the same component to manage multiple
pieces of state separately.
You can update state using the functional update form to safely work with the
previous state, especially in asynchronous scenarios:
jsx
setCount(prevCount => prevCount + 1);
Extended Examples
MS. ESHA VINAY PATEL 39
UNIT 2 : FUNDAMENTALS OF REACT.JS
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
Every click updates count and causes a re-render of Counter with new value.
jsx
setCount(prevCount => prevCount + 1);
jsx
function Counter() {
const [count, setCount] = useState(0);
function updateName(newName) {
setUser(prevUser => ({
...prevUser,
name: newName
}));
}
Use the spread operator ...prevUser to keep other properties unchanged when updating
one field.
This is because useState does not merge state updates, unlike class component setState.
Best Practices
Initialize state properly: If the initial state requires computation, pass a function to
useState so it runs only once:
jsx
const [value, setValue] = useState(() => computeInitialValue());
Avoid state mutations: Always give a new value or object to the setter; do not
mutate existing state.
Split state logically: Use multiple useState hooks for unrelated pieces of state rather
than a large object.
Use functional updates when the new state depends on the previous state.
Keep state minimal: Store only the necessary app data needed for rendering.
What is useEffect?
The useEffect hook lets you perform side effects in functional components.
Side effects include:
Data fetching from APIs
Setting up subscriptions or timers
Manually changing the DOM
Logging, analytics, or any operations that interact outside React rendering
It replaces lifecycle methods like componentDidMount, componentDidUpdate,
and componentWillUnmount in class components.
Syntax
jsx
useEffect(() => {
// Side effect code here
return () => {
// Optional cleanup code here
};
}, [dependencies]);
The first argument is a function containing side-effect logic.
The returned function, if any, is a cleanup function that runs before the effect is re-executed
or when the component unmounts.
The second argument is an array of dependencies. The effect runs:
After initial render
Then any time a value in the dependencies array changes
If the dependencies array is empty ([]), the effect runs only once after the component mounts.
How It Works
Example 1: Log every count update
jsx
import React, { useState, useEffect } from 'react';
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log(`Count changed to ${count}`);
}, [count]); // Runs this effect after count changes
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Add 1</button>
</div>
);
}
Every time count updates, the effect executes, logging the new count.
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const intervalId = setInterval(() => {
setSeconds(prev => prev + 1);
}, 1000);
What is useContext?
useContext allows functional components to consume context values directly.
React Context is a way to pass data through component trees without manually passing props
down at every level.
It solves prop drilling, where props are needlessly passed through intermediate components.
You create a context object and provide it via <Context.Provider>.
Any component (child, grandchild, etc.) can read the value via useContext.
Usage Steps
1. Create Context
jsx
import React from 'react';
function ChildComponent() {
const contextValue = useContext(MyContext);
return <div>User: {contextValue.user}</div>;
}
This avoids passing user explicitly through props.
function ThemedText() {
const theme = useContext(ThemeContext);
const style = {
function App() {
return (
<ThemeContext.Provider value="dark">
<ThemedText />
</ThemeContext.Provider>
);
}
ThemedText accesses context value directly, no props required.
Changing provider’s value changes all consumers automatically.
function Counter() {
const [count, setCount] = useState(0);
return (
<div style={{ margin: '1rem 0' }}>
<h3>Counter</h3>
<p>Current count: {count}</p>
<button onClick={() => setCount(count + 1)} style={{ marginRight: 8 }}>
Increase
</button>
<button onClick={() => setCount(count > 0 ? count - 1 : 0)}>
Decrease
</button>
</div>
);
}
function UserProfile() {
const { user, theme } = useContext(ThemeContext);
const style = {
padding: '1rem',
border: '1px solid',
borderColor: theme === 'dark' ? '#aaa' : '#444',
backgroundColor: theme === 'dark' ? '#222' : '#f9f9f9',
color: theme === 'dark' ? '#eee' : '#111',
};
return (
<div style={style}>
<h3>User Profile</h3>
<p>User Name: <strong>{user.name}</strong></p>
<p>Current Theme: <strong>{theme}</strong></p>
</div>
);
}
function App() {
const [theme, setTheme] = useState('light');
const [user, setUser] = useState({ name: 'Alice' });
const [time, setTime] = useState(new Date());
// useEffect runs once to set up a timer updating the current time every second
useEffect(() => {
const timerId = setInterval(() => {
setTime(new Date());
}, 1000);
return (
<ThemeContext.Provider value={{ theme, toggleTheme, user }}>
<div style={appStyle}>
<header>
<h1>React Hooks Combined Example</h1>
<p>Current time: {time.toLocaleTimeString()}</p>
</header>
{/* Components consuming context and managing their own state/effects */}
<UserProfile />
<Counter />
</div>
</ThemeContext.Provider>
);
}
</React.StrictMode>
);
Summary Table
Hook Usage Example in Project Role
useState Manage theme, user, counter local state State management inside App and Counter
Update time every second, log counter Side effects and cleanup (timers and
useEffect
changes logging)
Share theme and user data to any Access global/shared state without prop
useContext
component drilling
What is useRef?
useRef is a React Hook that returns a mutable ref object with a .current property.
This ref object persists for the full lifetime of the component.
Changing .current does not cause a component re-render, making it perfect for
storing mutable data that should survive between renders without triggering updates.
Primarily used to:
o Access and manipulate DOM elements directly.
o Hold mutable values that don’t cause re-renders when changed.
o Keep values between renders (similar to instance variables in class
components).
Sometimes, you need to interact with the underlying DOM element (input, div, etc.) directly
— for example, focusing an input or reading its current value.
You might want to keep track of some value that changes over time but should not trigger re-
render when updated (like timers, previous props, or mutable state).
useRef can hold any value, and that value will persist as long as the component is mounted.
Syntax
jsx
const refContainer = useRef(initialValue);
Examples
1. Accessing a DOM element (e.g., focusing an input)
jsx
import React, { useRef } from 'react';
function TextInputWithFocusButton() {
const inputRef = useRef(null);
return (
<>
<input ref={inputRef} type="text" placeholder="Click button to focus me" />
<button onClick={focusInput}>Focus Input</button>
</>
);
}
function PreviousCountExample() {
const [count, setCount] = useState(0);
const prevCountRef = useRef();
useEffect(() => {
// Store current count in ref after each render
prevCountRef.current = count;
});
return (
<div>
<h2>Now: {count}</h2>
<h2>Before: {prevCount}</h2>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
timerIdRef.current = setInterval(() => {
setSeconds(prev => prev + 1);
}, 1000);
Important Notes
Updating ref.current does not trigger re-render, so do not rely on it if UI update
is needed. For UI state, use useState.
useRef is similar to instance variables in class components.
Returning a ref from useRef ensures the same object persists across renders.
App.js
jsx
import React, { useState, useEffect, useRef } from 'react';
function App() {
// State for counter
const [count, setCount] = useState(0);
return (
<div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
<h2>useRef Hook Example</h2>
How to Run
1. Create a new React app or use an existing one created by:
bash
npx create-react-app my-app
cd my-app
2. Replace the content of src/App.js with the above code.
3. Start the React app:
bash
npm start
4. Open http://localhost:3000
Track previous state value Update prevCountRef.current = count in useEffect after render
What is useReducer?
useReducer is a React Hook that lets you manage complex state logic in function
components.
It is an alternative to useState and is preferred when:
o State depends on previous state values,
o You have multiple related state variables,
o Your state updates involve multiple sub-values or complex updates,
o You want to centralize state update logic in a single function.
It implements the Reducer pattern popular in Redux and functional programming: a
reducer function takes a current state and an action, and returns a new state.
Basic Syntax
jsx
const [state, dispatch] = useReducer(reducer, initialState);
reducer: A function (state, action) => newState that updates state based on an
action.
initialState: Initial value of the state.
state: Current state value.
dispatch: A function to send actions to the reducer.
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</div>
);
}
const initialState = {
username: '',
email: '',
password: '',
};
function SignupForm() {
const [state, dispatch] = useReducer(reducer, initialState);
const { username, email, password } = state;
function handleSubmit(e) {
e.preventDefault();
alert(`Signup info:\n${JSON.stringify(state, null, 2)}`);
dispatch({ type: 'reset' });
}
function handleChange(e) {
dispatch({
type: 'field',
field: e.target.name,
value: e.target.value,
});
}
return (
<form onSubmit={handleSubmit}>
<input name="username" value={username} onChange={handleChange} placeholder="Username" />
<input name="email" value={email} onChange={handleChange} placeholder="Email" />
<input name="password" type="password" value={password} onChange={handleChange}
placeholder="Password" />
<button type="submit">Sign Up</button>
</form>
);
}
Reducer
(state, action) => newState
function
Dispatch action dispatch({ type: 'increment' })
This example manages a simple counter with increment, decrement, and reset actions using
useReducer.
// Initial state
const initialState = { count: 0 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return initialState;
default:
function App() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div style={{ textAlign: 'center', marginTop: 50, fontFamily: 'Arial, sans-serif' }}>
<h1>Counter with useReducer</h1>
<p style={{ fontSize: 48 }}>{state.count}</p>
<button onClick={() => dispatch({ type: 'decrement' })}
style={{ padding: '10px 20px', fontSize: 20, marginRight: 10 }}>
-
</button>
<button onClick={() => dispatch({ type: 'increment' })}
style={{ padding: '10px 20px', fontSize: 20, marginRight: 10 }}>
+
</button>
<button onClick={() => dispatch({ type: 'reset' })}
style={{ padding: '10px 20px', fontSize: 20 }}>
Reset
</button>
</div>
);
}
You will see a counter initialized at 0, with three buttons: decrement (-), increment (+), and
reset. Clicking these buttons dispatches actions that update the state using the reducer
function.
Manages simple state (usually single Manages complex state logic, multiple
Purpose
values or simple objects) actions, or deeply nested updates
State update Central reducer function handles all
Setters passed inline or with callbacks
logic updates based on action types
When to Complex state transitions, related state
Simple/independent state variables
prefer variables depending on each other
Example To track counter with multiple actions
To track a single counter value
usage (increment, decrement, reset)
function CounterWithState() {
const [count, setCount] = useState(0);
return (
<div style={{ textAlign: 'center', marginTop: 50 }}>
<h1>Counter with useState</h1>
<p style={{ fontSize: 48 }}>{count}</p>
<button onClick={() => setCount(count - 1)}>-</button>
<button onClick={() => setCount(count + 1)}>+</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}
Summary
Use useState for simple and independent state variables.
Use useReducer when state logic is complex with multiple sub-values, complicated
updates, or when you want to centralize logic.
useReducer resembles Redux reducers but can be used locally in components without
external libraries.
useReducer makes it easier to handle multiple related state updates in one place and
improves maintainability.
What is useCallback?
The React useCallback hook is a built-in hook that returns a memoized callback
function. It helps optimize performance by preventing unnecessary re-creation of
functions on every render. This is especially useful when passing functions as props
to child components that rely on reference equality (like when these children are
wrapped in React.memo to avoid unnecessary re-renders).
Syntax
jsx
const memoizedCallback = useCallback(() => {
// Your function logic
}, [dependency1, dependency2]);
Simple Example
jsx
import React, { useState, useCallback } from 'react';
function App() {
const [count, setCount] = useState(0);
// Memoized callback - function identity stable unless dependencies (empty here) change
const increment = useCallback(() => {
setCount(c => c + 1);
}, []);
return (
<div>
<p>Count: {count}</p>
<Button onClick={increment}>Increment</Button>
</div>
);
Explanation:
Key Points
Aspect Description
What it does Memoizes a function to keep the same reference between renders
function App() {
const [count, setCount] = useState(0);
const [text, setText] = useState("");
return (
<div style={{ padding: 20, fontFamily: "Arial, sans-serif" }}>
<h1>useCallback with React.memo Demo</h1>
<div>
<p>Count: {count}</p>
<Button onClick={increment}>Increment</Button>
</div>
text
Button "Increment" rendered
When you type in the text input, since increment is memoized, the button does not
re-render (no log shown).
When you click the increment button, count changes and app re-renders—but Button
also doesn't re-render unnecessarily due to stable function prop.
5. Summary Table
Concept With useCallback Without useCallback
New function created each
Function prop identity Stable, same function instance
render
Child re-render with
Prevented if props unchanged Triggered on every parent render
React.memo
What is useMemo?
The React useMemo hook is used to memoize the result of an expensive function or
calculation so that it only recomputes when its dependencies change. This optimization helps
avoid costly recalculations on every render and can improve performance, especially in
components with heavy computations or when passing computed values to pure components.
Syntax
jsx
const memoizedValue = useMemo(() => {
// expensive calculation here
return computeValue;
}, [dependency1, dependency2]);
The function inside useMemo runs on first render and when any dependency changes.
Otherwise, it returns the cached value from the previous render.
function isPrime(num) {
for (let i = 2; i <= Math.sqrt(num); i++) {
if (num % i === 0) return false;
}
return num > 1;
}
function PrimeCalculator() {
const [number, setNumber] = useState(100000);
const [inc, setInc] = useState(0);
return (
<div>
<input
type="number"
value={number}
onChange={e => setNumber(Number(e.target.value))}
/>
<p>Primes up to {number} calculated: {primes.length}</p>
<button onClick={() => setInc(inc + 1)}>Re-render {inc}</button>
</div>
);
}
Keeping function references stable when passing them as props to child components
wrapped with React.memo, preventing unnecessary re-renders.
function App() {
const [items, setItems] = useState(['Item 1', 'Item 2']);
const [multiplier, setMultiplier] = useState(1);
return (
<div>
<h3>useCallback & useMemo Example</h3>
<label>
Multiplier:
<input
type="number"
value={multiplier}
onChange={(e) => setMultiplier(Number(e.target.value))}
/>
</label>
Summary Table
Hook Purpose Memoizes
1. App.js
jsx
import React, { useState, useMemo, useCallback } from 'react';
function App() {
const [items, setItems] = useState(['Item 1', 'Item 2']);
const [multiplier, setMultiplier] = useState(1);
}, [items, multiplier]);
return (
<div style={{ padding: 20, fontFamily: 'Arial, sans-serif' }}>
<h1>useMemo & useCallback Example</h1>
{/* Pass memoized items and callback to memoized List component */}
<List items={multipliedItems} onAdd={handleAdd} />
</div>
);
}
2. How to Run
1. Create a new React project (if you don't have one):
bash
npx create-react-app memo-callback-demo
cd memo-callback-demo
bash
npm start
text
4. Key Takeaways
Concept Benefit in Example
Memoizes the calculated list so it only recalculates when dependencies change,
useMemo
optimizing rendering.
Memoizes the onAdd callback function so it has stable identity, preventing
useCallback
unnecessary child component renders.
React.memo Memoizes the List component to avoid re-rendering on unchanged props.
A custom hook in React is a JavaScript function whose name starts with use and which
allows you to extract and reuse stateful logic across multiple components. Custom hooks
enable you to share behavior without repeating code, making your components cleaner, more
modular, and easier to maintain.
jsx
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
setError(null);
fetch(url)
.then((res) => {
if (!res.ok) {
throw new Error('Network response was not ok');
}
return res.json();
})
.then((data) => {
setData(data);
setLoading(false);
})
.catch((error) => {
setError(error.message);
setLoading(false);
});
}, [url]);
Usage in a component:
jsx
import React from 'react';
import useFetch from './useFetch';
function Todos() {
const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/todos');
return (
<ul>
{data.map(todo => <li key={todo.id}>{todo.title}</li>)}
</ul>
);
}
Summary Table
Aspect Description
Aspect Description
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
if (!url) return;
setLoading(true);
setError(null);
fetch(url)
.then((response) => {
if (!response.ok) throw new Error('Network response was not ok');
return response.json();
})
.then((json) => {
setData(json);
setLoading(false);
})
.catch((err) => {
setError(err.message);
setLoading(false);
});
}, [url]);
function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
useEffect(() => {
try {
window.localStorage.setItem(key, JSON.stringify(storedValue));
} catch {
// Ignore write errors
}
}, [key, storedValue]);
function App() {
// Using useFetch to get todo items
const { data: todos, loading, error } = useFetch('https://jsonplaceholder.typicode.com/todos?_limit=5');
return (
<div style={{ fontFamily: 'Arial, sans-serif', padding: 20 }}>
<h1>Custom Hooks Demo</h1>
<hr />
<hr />
/>
</label>
{name && <p>Hello, {name}!</p>}
</section>
</div>
);
}
function useCustomFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
if (!url) return;
setLoading(true);
setError(null);
fetch(url)
.then(res => {
if (!res.ok) throw new Error('Fetch error');
return res.json();
})
.then(json => {
setData(json);
setLoading(false);
})
.catch(err => {
setError(err.message);
setLoading(false);
});
}, [url]);
function Counter() {
const [count, setCount] = useState(0);
const [state, dispatch] = useReducer(reducer, initialState);
const prevCountRef = useRef();
useEffect(() => {
prevCountRef.current = count; // Track previous count
}, [count]);
useEffect(() => {
console.log(`useReducer count changed: ${state.count}`);
}, [state.count]);
return (
<div>
<h2>useState Count: {count}</h2>
<button onClick={() => setCount(c => c + 1)}>Increment useState</button>
<p>Previous useState Count: {prevCountRef.current || 0}</p>
5. UserProfile.js (useContext)
jsx
import React, { useContext } from 'react';
import { ThemeContext } from './ThemeContext';
function UserProfile() {
const { user, theme, toggleTheme } = useContext(ThemeContext);
return (
<div style={{
padding: '1rem',
backgroundColor: theme === 'dark' ? '#222' : '#eee',
color: theme === 'dark' ? '#eee' : '#111',
borderRadius: 4,
marginBottom: 20,
}}>
<h3>User Profile</h3>
<p>Name: {user.name}</p>
<p>Theme: {theme}</p>
<button onClick={toggleTheme}>Toggle Theme</button>
</div>
);
}
function App() {
const [theme, setTheme] = useState('light');
const [user, setUser] = useState({ name: 'Alice' });
const { data, loading, error } = useCustomFetch('https://jsonplaceholder.typicode.com/todos?_limit=3');
const [multiplier, setMultiplier] = useState(1);
const [items, setItems] = useState(['Item 1', 'Item 2']);
// Memoized handler
const appStyle = {
padding: 20,
backgroundColor: theme === 'dark' ? '#121212' : '#fafafa',
color: theme === 'dark' ? '#eee' : '#222',
minHeight: '100vh',
fontFamily: 'Arial, sans-serif',
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme, user }}>
<div style={appStyle}>
<h1>React.js Fundamentals Combined Example</h1>
<hr />
<hr />
7. index.js
jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';