Framework and Libraries
5.1 Introduction of Node.js
5.2 Getting up React environment, Create React App
5.3 Hello World, Components, JSX
5.4 Functional vs class components, Props
5.5 State, Lifecycle methods
5.6 Hooks – useState, useEffect, useContext
5.7 Event handling
5.8 Forms – controlled components, submission,
validation
5.9 Conditional rendering – if, ternary, &&
5.10 Lists and keys, Importance of keys
5.11 Styling – CSS, CSS Modules, CSS-in-JS
5.12 React Router – setup, routes, parameters
5.13 Async/await, Promises, Fetch API
📘 5.1 Introduction to Node.js
Node.js is a JavaScript runtime built on Chrome's V8 engine that
allows you to run JavaScript outside the browser, mainly used for
building server-side applications.
🚀 Key Features of Node.js:
Feature Description
Asynchronous & Handles multiple requests without waiting for one
Non-blocking I/O to finish. Great for scalability.
Event-driven Uses events and callbacks for handling tasks like
architecture reading files or processing HTTP requests.
Single-threaded but Uses a single thread but can handle thousands of
scalable requests using event loop.
Uses Google Chrome’s V8 engine to compile
Fast execution
JavaScript into native machine code.
Cross-platform Runs on Windows, Mac, Linux.
Feature Description
NPM (Node Package Includes over 1.5 million open-source
Manager) packages/libraries for rapid development.
💡 Use Cases of Node.js:
Web servers & APIs (RESTful services)
Real-time applications (e.g., chat apps, multiplayer games)
File system tools
Microservices
Command-line tools
🧱 Node.js Architecture:
V8 Engine – Compiles JS to machine code
Libuv – Handles asynchronous I/O using an event loop
Event Loop – Manages incoming events & executes callbacks
C++ bindings – Interface between JS and OS-level APIs
⚛️Getting Up the React Environment & Creating a React App
To build React apps, you need to set up a development environment first.
The easiest way to start is with Create React App (CRA).
✅ Step-by-Step: Setup React with Create React App
🔧 1. Install Node.js (if not already installed)
React needs Node.js and npm (Node Package Manager).
Download from: https://nodejs.org
Check versions:
node -v
npm -v
⚙️2. Create a New React App
npx create-react-app my-app
npx is a tool that comes with npm, used to run commands.
my-app is your project folder name (you can change it).
💡 This sets up everything: React, Webpack, Babel, project structure, etc.
📁 3. Project Structure Overview
my-app/
├── public/
│ └── index.html // Root HTML file
├── src/
│ ├── App.js // Main React component
│ └── index.js // App entry point
├── package.json // Project dependencies and scripts
▶️4. Run Your App
Navigate to your app folder and run:
cd my-app
npm start
This will open http://localhost:3000 in your browser.
You'll see the default React welcome screen.
✅ 5. Edit and Build
Open src/App.js and modify the component to start building your
app.
To build your project for production:
npm run build
⚛️5.3 Hello World, Components, and JSX in React
✅ 1. Hello World in React
After setting up your React environment using create-react-app, go to:
📄 src/App.js and replace the code with:
import React from 'react';
function App() {
return (
<div>
<h1>Hello World from React!</h1>
</div>
);
export default App;
Then run:
npm start
You’ll see “Hello World from React!” in your browser.
🧱 2. Components in React
➤ What is a Component?
A component is a reusable piece of UI. There are two types:
Functional Components (most common)
Class Components (older way)
✅ Functional Component Example:
function Greeting() {
return <h2>Hello from Greeting Component</h2>;
Use it in App.js:
function App() {
return (
<div>
<Greeting />
</div>
);
🔤 3. JSX (JavaScript XML)
JSX is a syntax extension for JavaScript that looks like HTML but is used
in React.
✅ Example of JSX:
const name = "Alice";
const element = <h1>Hello, {name}!</h1>;
It allows embedding JavaScript inside curly braces {}:
function App() {
const name = "Bob";
return <h2>Welcome, {name}</h2>;
💡 JSX Rules:
Always return a single root element
Use className instead of class
Use {} for dynamic content
⚛️4. Functional vs Class Components + Props
In React, you build UI using components, and you pass data between
them using props (properties).
✅ What Are Props?
Props are short for "properties"
They allow you to pass data from parent to child components
Props are read-only
🧩 Functional Component with Props
// GreetingFunctional.js
import React from 'react';
function GreetingFunctional(props) {
return <h2>Hello, {props.name}!</h2>;
export default GreetingFunctional;
✅ You can also use destructuring to simplify:
function GreetingFunctional({ name }) {
return <h2>Hello, {name}!</h2>;
}
🧱 Class Component with Props
// GreetingClass.js
import React, { Component } from 'react';
class GreetingClass extends Component {
render() {
return <h2>Hello, {this.props.name}!</h2>;
export default GreetingClass;
🧪 Using Both in App
// App.js
import React from 'react';
import GreetingFunctional from './GreetingFunctional';
import GreetingClass from './GreetingClass';
function App() {
return (
<div>
<GreetingFunctional name="Alice" />
<GreetingClass name="Bob" />
</div>
);
export default App;
🧾 Output
Hello, Alice! // from functional component
Hello, Bob! // from class component
🆚 Summary Table
Functional
Feature Class Component
Component
class + extends
Syntax Function
React.Component
props.name or
Props Access this.props.name
{ name }
State ✅ useState Hook
✅ this.state & this.setState()
Management (modern)
❌ Legacy / rarely used in new
Recommended ✅ Yes (modern apps)
code
Here’s a simplified explanation of 5.5: State and Lifecycle Methods in
Functional Components using React Hooks (useState, useEffect):
✅ What is State?
State is a built-in object in React used to store and manage data
inside a component.
It updates the UI whenever its value changes.
Example: Counter with useState
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0); // initial state is 0
return (
<div>
<h2>Count: {count}</h2>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
export default Counter;
🔁 What are Lifecycle Methods in Functional Components?
Functional components use useEffect() instead of lifecycle methods
(like componentDidMount, componentDidUpdate,
componentWillUnmount).
🔹 1. Run once when component mounts (like componentDidMount)
useEffect(() => {
console.log("Component mounted");
}, []); // empty dependency array means only once
🔹 2. Run when state/props update (like componentDidUpdate)
useEffect(() => {
console.log("Count updated");
}, [count]); // runs only when `count` changes
🔹 3. Cleanup before component unmounts (like
componentWillUnmount)
useEffect(() => {
const timer = setInterval(() => {
console.log("Running...");
}, 1000);
return () => {
clearInterval(timer); // cleanup
console.log("Component unmounted");
};
}, []);
Example:
import React, { Component } from 'react';
class StateLifeCycle extends Component {
constructor() {
super();
this.state = { count: 0 };
console.log('Constructor: Component is being created');
componentDidMount() {
console.log('Mounted: Component is on the screen');
componentDidUpdate() {
console.log('Updated: State or props changed');
componentWillUnmount() {
console.log('Unmounting: Component is being removed');
increase = () => {
this.setState({ count: this.state.count + 1 });
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increase}>Counter Increment</button>
</div>
);
export default StateLifeCycle;
🧾 Summary
Feature Hook / Concept
Local state useState()
On mount useEffect(() => {}, [])
useEffect(() => {},
On update
[dependency])
On unmount return () => {} inside
(cleanup) useEffect
1. useState Hook
Purpose: The useState hook allows you to add state to functional
components. It returns an array with two values: the current state
and a function to update the state.
Example: Counter with useState
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0); // state with initial value 0
return (
<div>
<h2>Count: {count}</h2>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
export default Counter;
useState(0): Initializes the count to 0. The first value (count) holds
the current state, and setCount is the function to update that state.
setCount(count + 1): Updates the state by increasing the count.
1. Using useEffect to Fetch API Data
Here, we will use the useEffect hook to fetch data from an API when the
component mounts. For simplicity, we will use the JSONPlaceholder API to
get some sample posts.
Example: Fetching Data from an API with useEffect
import React, { useState, useEffect } from 'react';
function Posts() {
const [posts, setPosts] = useState([]); // State to hold the fetched posts
const [loading, setLoading] = useState(true); // State to track loading
state
// useEffect to fetch data from API when the component mounts
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json()) // Convert response to JSON
.then(data => {
setPosts(data); // Store the data in state
setLoading(false); // Set loading to false once data is fetched
})
.catch(error => console.error('Error fetching data:', error)); // Error
handling
}, []); // Empty array means this effect runs once when the component
mounts
return (
<div>
<h2>Posts</h2>
{loading ? (
<p>Loading...</p>
):(
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li> // Display the title of each
post
))}
</ul>
)}
</div>
);
export default Posts;
Key Points in this Example:
useEffect: Used to fetch data when the component mounts.
setPosts: Updates the state with the fetched posts.
setLoading: Controls the loading state to show a "Loading..."
message while the data is being fetched.
fetch: Makes an HTTP request to fetch data from an API.
Sure! Here’s a much simpler example of using useContext to share a value
(like a simple "counter") across components in React.
Example: Simple Counter with useContext
Step 1: Create the Context
We’ll first create a context to share the counter state.
import React, { createContext, useState } from 'react';
// Create a context to manage the counter value
const CounterContext = createContext();
// Create a provider component that will wrap the app
export function CounterProvider({ children }) {
const [counter, setCounter] = useState(0); // Initial counter value
const increment = () => {
setCounter(counter + 1); // Increment the counter
};
const decrement = () => {
setCounter(counter - 1); // Decrement the counter
};
return (
<CounterContext.Provider value={{ counter, increment,
decrement }}>
{children}
</CounterContext.Provider>
);
}
export default CounterContext;
Step 2: Consume the Context in a Component
Now, let’s use useContext in a component to read and update the counter
state.
import React, { useContext } from 'react';
import { CounterContext } from './CounterContext'; // Import the context
function Counter() {
const { counter, increment, decrement } = useContext(CounterContext);
// Get the context values
return (
<div>
<h2>Counter: {counter}</h2>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
export default Counter;
Step 3: Use the CounterProvider in Your App
Finally, we’ll wrap the entire app in the CounterProvider to give access to
the context.
import React from 'react';
import { CounterProvider } from './CounterContext'; // Import the provider
import Counter from './Counter'; // Import the Counter component
function App() {
return (
<CounterProvider>
<Counter />
</CounterProvider>
);
export default App;
Explanation:
1. CounterContext: This is where we create the context that will
manage the counter state.
2. useState: We use useState to initialize and manage the counter
value.
3. useContext: In the Counter component, we use useContext to
access the shared counter value and the increment and decrement
functions.
4. CounterProvider: This wraps the app and provides the context to all
components inside it, so they can access the counter state.
How it works:
When you click the Increment button, the counter increases by 1.
When you click the Decrement button, the counter decreases by 1.
The CounterContext allows the counter value to be shared between
components, and useContext lets us access and update that value
easily.
5.7 Event Handling in React
In React, handling events is similar to handling events in traditional
JavaScript, but there are some key differences. React has its own synthetic
event system, which is a cross-browser wrapper around the browser's
native events. This system works similarly to the DOM events but has the
advantage of being normalized.
Here's a breakdown of event handling in React:
1. Basic Event Handling in React
In React, you can add event listeners using the camelCase syntax (e.g.,
onClick, onChange, onSubmit) instead of the lowercase syntax used in
plain HTML.
Example: Simple Button Click Event
import React, { useState } from 'react';
function App() {
const [message, setMessage] = useState('Hello World');
// Event handler function
const handleClick = () => {
setMessage('Button was clicked!');
};
return (
<div>
<h1>{message}</h1>
<button onClick={handleClick}>Click Me</button>
</div>
);
export default App;
Explanation:
onClick: The onClick event is used to trigger the handleClick
function when the button is clicked.
useState: We use useState to manage the state (message) and
update it when the button is clicked.
Event Handler: The handleClick function updates the message
when the button is clicked.
2. Event Handlers with Parameters
Sometimes, you might want to pass arguments to the event handler
function.
Example: Passing Arguments to Event Handler
import React from 'react';
function App() {
const handleClick = (name) => {
alert(`Hello, ${name}!`);
};
return (
<div>
<button onClick={() => handleClick('Alice')}>Say Hello to
Alice</button>
<button onClick={() => handleClick('Bob')}>Say Hello to
Bob</button>
</div>
);
export default App;
Explanation:
The handleClick function accepts a name argument, and we use an
arrow function inside the onClick event to pass the argument when
calling handleClick.
Key Concepts of Event Handling in React:
1. Synthetic Events: React uses a synthetic event system that wraps
the browser’s native events.
2. CamelCase Naming: In React, event names are written in
camelCase, like onClick, onChange, etc.
3. Event Binding: In class components, event handlers need to be
explicitly bound to the component instance. This is not necessary in
functional components.
4. Handling State: In event handlers, we can update the component
state using useState (in functional components) or this.setState (in
class components).
5. Form Events: Common form events include onChange, onSubmit,
and onInput.
5.8 Forms – Controlled Components, Submission, and Validation in
React
In React, forms can be handled using controlled components. A controlled
component is an input element whose value is controlled by the state in
React. React takes care of keeping the form fields in sync with the
component state, making it easier to handle form submissions and
validations.
Key Concepts:
Controlled Components: Form elements whose values are
controlled by the component’s state.
Uncontrolled Components: Form elements that manage their own
state, and React does not manage the input state.
Form Validation: Ensuring the data entered into a form is valid
before submission.
1. Controlled Components
A controlled component is an input field whose value is controlled by
React's state. This allows React to manage the state of form fields, making
it easier to handle form submissions and validations.
Example: Controlled Input Field
import React, { useState } from 'react';
function App() {
const [name, setName] = useState('');
// Handle input change
const handleChange = (event) => {
setName(event.target.value);
};
// Handle form submission
const handleSubmit = (event) => {
event.preventDefault();
alert('Form submitted with name: ' + name);
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={name} // Controlled by React state
onChange={handleChange} // Update state on input change
placeholder="Enter your name"
/>
<button type="submit">Submit</button>
</form>
);
export default App;
Explanation:
value={name}: The input's value is bound to the name state,
making it a controlled component.
onChange={handleChange}: Updates the state whenever the
user types in the input field.
handleSubmit: Handles the form submission, preventing the
default behavior and showing the submitted value.
2. Form Submission
In React, when handling form submissions, you typically prevent the
default form submission behavior using event.preventDefault(). This
allows you to handle the form submission manually and update the state
or perform any other actions you need.
Example: Form Submission Handling
import React, { useState } from 'react';
function App() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleEmailChange = (event) => setEmail(event.target.value);
const handlePasswordChange = (event) =>
setPassword(event.target.value);
const handleSubmit = (event) => {
event.preventDefault();
// Form submission logic here
alert(`Submitted: Email - ${email}, Password - ${password}`);
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={handleEmailChange}
placeholder="Enter your email"
/>
<input
type="password"
value={password}
onChange={handlePasswordChange}
placeholder="Enter your password"
/>
<button type="submit">Submit</button>
</form>
);
export default App;
Explanation:
onSubmit={handleSubmit}: The form submission is controlled by
React.
event.preventDefault(): Prevents the default form submission
action (which would reload the page), allowing us to handle it in
React.
3. Form Validation
Form validation is important to ensure that the user enters the correct
data before submitting the form. React provides a way to validate input
fields by checking the values in state before submitting the form.
Example: Form with Validation
import React, { useState } from 'react';
function App() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const handleEmailChange = (event) => setEmail(event.target.value);
const handlePasswordChange = (event) =>
setPassword(event.target.value);
const handleSubmit = (event) => {
event.preventDefault();
// Basic validation
if (!email || !password) {
setError('Both fields are required');
} else {
setError('');
alert(`Form submitted: Email - ${email}, Password - ${password}`);
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={handleEmailChange}
placeholder="Enter your email"
/>
<input
type="password"
value={password}
onChange={handlePasswordChange}
placeholder="Enter your password"
/>
{error && <p style={{ color: 'red' }}>{error}</p>}
<button type="submit">Submit</button>
</form>
</div>
);
export default App;
Explanation:
Validation Logic: Before submitting the form, we check if both the
email and password are entered. If not, we show an error message.
Error Handling: The error message is conditionally rendered if
there is an error in the form.
Summary
1. Controlled Components: In React, controlled components have
their values tied to state. This makes it easy to manage form inputs
and ensure that React always has control over the data.
2. Form Submission: You handle form submission using onSubmit
and prevent the default browser behavior using
event.preventDefault().
5.9 Conditional Rendering in React – if, Ternary, &&
Conditional rendering in React allows you to render different UI elements
or components based on certain conditions. It can be done using several
approaches:
1. if Statements: Using traditional if conditions to control rendering.
2. Ternary Operator (? :): A compact way to render components or
elements based on a condition.
3. Logical AND (&&): A simple way to render something only if a
condition is true.
Let's look at examples for each of these methods.
1. Using if Statements for Conditional Rendering
In React, you can use the if statement to conditionally render parts of the
component. However, you can't directly use if statements inside the JSX;
you need to handle the condition before the return statement.
Example: if Statement
import React, { useState } from 'react';
function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
let content;
if (isLoggedIn) {
content = <h1>Welcome Back!</h1>;
} else {
content = <h1>Please log in</h1>;
return (
<div>
{content}
<button onClick={() => setIsLoggedIn(!isLoggedIn)}>
{isLoggedIn ? 'Log Out' : 'Log In'}
</button>
</div>
);
export default App;
Explanation:
if Statement: We use an if statement to check whether the user is
logged in (isLoggedIn). Based on that, we set the content variable to
display the appropriate message.
Toggling State: The button toggles between "Log In" and "Log Out"
based on the isLoggedIn state.
2. Using the Ternary Operator for Conditional Rendering
The ternary operator is a more concise way to write conditional logic in
React. It has the following syntax:
condition ? expression_if_true : expression_if_false;
Example: Ternary Operator
import React, { useState } from 'react';
function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
return (
<div>
{isLoggedIn ? <h1>Welcome Back!</h1> : <h1>Please log in</h1>}
<button onClick={() => setIsLoggedIn(!isLoggedIn)}>
{isLoggedIn ? 'Log Out' : 'Log In'}
</button>
</div>
);
export default App;
Explanation:
Ternary Operator: We use the ternary operator to display either
"Welcome Back!" or "Please log in" based on the isLoggedIn state.
Toggling State: The button toggles between "Log In" and "Log Out"
based on the isLoggedIn state.
3. Using Logical AND (&&) for Conditional Rendering
The logical AND (&&) operator can also be used for conditional rendering.
It's commonly used when you want to render something only if a condition
is true.
Example: Logical AND (&&) Operator
import React, { useState } from 'react';
function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
return (
<div>
{isLoggedIn && <h1>Welcome Back!</h1>} {/* Renders only if
isLoggedIn is true */}
<button onClick={() => setIsLoggedIn(!isLoggedIn)}>
{isLoggedIn ? 'Log Out' : 'Log In'}
</button>
</div>
);
export default App;
Explanation:
&& Operator: The text "Welcome Back!" is only rendered if
isLoggedIn is true. If it's false, nothing is rendered in its place.
Toggling State: The button toggles between "Log In" and "Log Out"
based on the isLoggedIn state.
Summary of Conditional Rendering Methods:
1. if Statement: Used when you need to perform more complex
conditions or multiple lines of logic before rendering JSX.
o Best for multi-line logic before the JSX.
2. Ternary Operator (? :): A compact form of if-else logic for inline
conditional rendering.
o Best for simple two-way conditions (if/else).
3. Logical AND (&&): Renders a component only if a condition is true,
otherwise renders nothing.
o Best for rendering something conditionally without
needing an "else" case.
5.10 Lists and Keys in React
In React, rendering lists of elements is a common operation. To efficiently
update and manage lists, React requires a key for each item in the list.
The key helps React identify which items have changed, been added, or
removed, making the update process more efficient.
Rendering Lists in React
In React, we use the map() function to render lists of elements. The map()
function iterates over an array and returns a new array of elements that
can be rendered in JSX.
Example: Rendering a List without Keys
import React from 'react';
function App() {
const fruits = ['Apple', 'Banana', 'Orange', 'Mango'];
return (
<div>
<h1>Fruits List</h1>
<ul>
{fruits.map(fruit => (
<li>{fruit}</li> // No key here
))}
</ul>
</div>
);
export default App;
Why We Need Keys
When rendering lists in React, keys help React efficiently update and
render the correct components when the list changes. Without keys, React
would have to re-render all the list items, which can negatively affect
performance, especially for large lists.
Keys need to be:
Unique: Each element in the list must have a unique key.
Stable: The key should not change between renders.
Consistent: It should uniquely identify an element across the list.
Importance of Keys
1. Efficient Re-rendering: React uses keys to optimize rendering and
to determine which elements need to be updated. Without keys,
React would have to compare all items in the list, which can be
inefficient.
2. Tracking Elements: Keys help React track individual elements in
the list. This helps React understand which items have been
modified, added, or removed, so it can efficiently re-render only the
affected parts.
3. Optimized DOM Updates: With keys, React can perform minimal
DOM manipulations, ensuring better performance, especially with
large lists or dynamic data.
Rendering Lists with Keys
To add keys to the elements in the list, you pass the key prop to each
element in the list. The key should be a unique identifier for each item.
Example: Rendering a List with Keys
import React from 'react';
function App() {
const fruits = ['Apple', 'Banana', 'Orange', 'Mango'];
return (
<div>
<h1>Fruits List</h1>
<ul>
{fruits.map((fruit, index) => (
<li key={index}>{fruit}</li> // Each item has a unique key
))}
</ul>
</div>
);
export default App;
Explanation:
The key={index} is used to assign a unique identifier to each list
item. In this case, we're using the index of the item in the array as
the key. However, using the index as the key is not recommended if
the order of items might change, as it can lead to issues when React
compares the keys.
When to Use Keys
When you are rendering a list of elements dynamically.
When the list is dynamic and items can be added, removed, or
reordered.
When you need efficient updates to the list when the data
changes.
Best Practices for Keys
1. Use a Unique ID: The best practice is to use a unique ID from
your data (e.g., an ID from a database or a generated UUID). This is
more reliable than using array indices, especially when items are
removed or reordered.
Example:
import React from 'react';
function App() {
const fruits = [
{ id: 1, name: 'Apple' },
{ id: 2, name: 'Banana' },
{ id: 3, name: 'Orange' },
{ id: 4, name: 'Mango' }
];
return (
<div>
<h1>Fruits List</h1>
<ul>
{fruits.map(fruit => (
<li key={fruit.id}>{fruit.name}</li> // Use unique ID as key
))}
</ul>
</div>
);
export default App;
Why this is better:
o Using a unique id as the key ensures that each item is
uniquely identified and remains stable across renders, even if
the list changes (items are added, removed, or reordered).
2. Avoid Using Array Index as Key: If the list changes (e.g., items
are added or removed), using the index as the key can cause
rendering bugs because the index does not uniquely represent the
item if the list is modified. This can lead to incorrect state being
retained between renders.
Example of why it's not recommended:
// This can cause issues when list items are reordered
<li key={index}>{fruit.name}</li>
How Keys Help in List Updates
Consider the following example, where a fruit is removed from the list:
import React, { useState } from 'react';
function App() {
const [fruits, setFruits] = useState([
{ id: 1, name: 'Apple' },
{ id: 2, name: 'Banana' },
{ id: 3, name: 'Orange' },
{ id: 4, name: 'Mango' }
]);
const removeFruit = (id) => {
setFruits(fruits.filter(fruit => fruit.id !== id));
};
return (
<div>
<h1>Fruits List</h1>
<ul>
{fruits.map(fruit => (
<li key={fruit.id}>
{fruit.name}
<button onClick={() => removeFruit(fruit.id)}>Remove</button>
</li>
))}
</ul>
</div>
);
export default App;
Explanation:
Removing an Item: When you remove a fruit from the list, React
uses the key to track the items. React efficiently updates the list
without having to re-render the entire list, only removing the correct
item.
Summary
Keys are essential for React to manage list elements efficiently and
perform minimal DOM updates.
Unique Keys should be used for each element in a list (e.g.,
database IDs or unique values).
Avoid using array indices as keys unless you are sure the list won't
change in terms of order.
Using keys improves performance and ensures that React can
efficiently update the DOM when the list changes.
Let me know if you need further clarification or examples!
5.11 Styling in React – CSS, CSS Modules, CSS-in-JS
Styling is a crucial aspect of any web application, and in React, there are
several ways to style components. The most common approaches include
CSS, CSS Modules, and CSS-in-JS.
Let's dive into each of these styling approaches in React:
1. CSS (Traditional CSS)
In traditional CSS, you create global stylesheets and link them to your
React components. The styles are applied globally across the application,
so they can affect all components unless scoped specifically.
Example of Global CSS Styling:
// App.css
.button {
background-color: blue;
color: white;
padding: 10px;
border-radius: 5px;
// App.js
import React from 'react';
import './App.css'; // Importing the CSS file
function App() {
return (
<div>
<button className="button">Click Me</button>
</div>
);
export default App;
Advantages of Global CSS:
Simple and easy to use.
Well-known approach for most web developers.
Disadvantages of Global CSS:
Global namespace: It can lead to style conflicts, as all styles are
globally scoped.
Harder to maintain: As your app grows, managing large CSS files
can become difficult.
2. CSS Modules
CSS Modules allow you to scope your CSS to the specific component. Each
component gets a unique class name, ensuring that the styles do not
conflict with other components.
Example of CSS Modules:
1. Create a CSS Module:
You create a CSS file with a .module.css extension.
/* Button.module.css */
.button {
background-color: green;
color: white;
padding: 10px;
border-radius: 5px;
2. Import and Use the CSS Module:
In the React component, you import the CSS module and apply the
styles using the imported object.
// Button.js
import React from 'react';
import styles from './Button.module.css'; // Import CSS module
function Button() {
return (
<button className={styles.button}>Click Me</button> {/* Use class
name from module */}
);
}
export default Button;
Advantages of CSS Modules:
Scoped styles: Styles are scoped to the component, avoiding
conflicts.
Automatic class name hashing: React automatically generates
unique class names.
Easier to maintain: Styles are scoped to components, making it
easier to understand and modify.
Disadvantages of CSS Modules:
It requires the CSS to be modularized, so you can't use global CSS
easily unless you use global selectors within the module.
Limited flexibility when applying certain styles globally.
3. CSS-in-JS
CSS-in-JS allows you to write CSS directly inside your JavaScript files, and
the styles are scoped to components. This approach is useful when you
need dynamic styling or want to style components conditionally.
There are several popular libraries for CSS-in-JS, like styled-
components, emotion, and JSS. Here's an example using styled-
components.
Example of CSS-in-JS using styled-components:
First, install the styled-components library:
npm install styled-components
Then, you can write your CSS inside your JavaScript using tagged
template literals.
// App.js
import React from 'react';
import styled from 'styled-components'; // Import styled-components
library
// Create a styled button component
const Button = styled.button`
background-color: orange;
color: white;
padding: 10px;
border-radius: 5px;
&:hover {
background-color: darkorange;
`;
function App() {
return (
<div>
<Button className={{backgroundColor:”blue”}}>Click
Me</Button> {/* Styled button */}
</div>
);
export default App;
Advantages of CSS-in-JS:
Dynamic styling: You can pass props to style components
conditionally based on component state or other logic.
Scoped styling: Styles are scoped to the component, so you don't
have to worry about class name conflicts.
JavaScript-powered: Styles can be directly integrated with
JavaScript, making it easy to handle dynamic themes and
transitions.
Disadvantages of CSS-in-JS:
Performance: It might affect performance due to runtime style
generation, especially if the styles are very dynamic.
Increased Bundle Size: Because the CSS is included in JavaScript
files, it can increase the size of the JavaScript bundle.
Learning Curve: You may need to learn a new library like styled-
components or emotion.
Comparison of Approaches
CSS CSS-in-JS (styled-
Feature CSS Modules
(Traditional) components)
Scope of Component-
Global Component-level
Styles level
Ease of Use Easy Moderate Moderate
Dynamic Not possible Possible with Highly possible (conditional
Styling directly inline styles styling with props)
Class Name
Possible No collision No collision
Collision
Depends on
Performance Efficient Can be affected by runtime
size of CSS
Easy to maintain, but
Can be hard as Easy to
Maintenance requires understanding
app grows maintain
CSS-in-JS libraries
Conclusion
Traditional CSS is the simplest approach but may lead to issues
with global namespace and maintainability.
CSS Modules are great for scoping styles to components and
avoiding style conflicts.
CSS-in-JS is the most flexible approach, enabling dynamic styling
and scoped styles directly within your JavaScript code.
Choosing between these approaches depends on the complexity of your
project and your team's preferences. For small projects, traditional CSS or
CSS Modules might be sufficient, but for larger, more dynamic
applications, CSS-in-JS can offer powerful features.
Let me know if you'd like any further details!
5.12 React Router – Setup, Routes, Parameters
React Router is a standard library used for routing in React applications. It
enables navigation between views (components) and allows for a
seamless, single-page application (SPA) experience.
React Router Setup:
1. Install React Router: First, install react-router-dom in your React
project.
bash
CopyEdit
npm install react-router-dom
2. Basic Setup:
Import the necessary components from react-router-dom to define routes
and navigation.
// App.js
import React from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
// Components
function Home() {
return <h2>Home Page</h2>;
function About() {
return <h2>About Page</h2>;
function User({ match }) {
return <h2>User ID: {match.params.userId}</h2>;
}
function App() {
return (
<Router>
<nav>
<Link to="/">Home</Link> |
<Link to="/about">About</Link> |
<Link to="/user/1">User 1</Link>
</nav>
{/* Directly render routes without using Switch */}
<Route path="/" exact component={Home} />
<Route path="/about" component={About} />
<Route path="/user/:userId" component={User} />
</Router>
);
export default App;
2. Async/Await – Fetching Data
Example using async/await to fetch data from an API:
import React, { useState, useEffect } from 'react';
function App() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchData() {
try {
const response = await
fetch("https://jsonplaceholder.typicode.com/posts");
const data = await response.json();
setData(data);
setLoading(false);
} catch (error) {
console.error("Error fetching data:", error);
setLoading(false);
fetchData();
}, []);
if (loading) return <p>Loading...</p>;
return (
<div>
<h1>Posts</h1>
<ul>
{data.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
export default App;
3. Error Handling
Handling errors with try/catch block in async code:
import React, { useState, useEffect } from 'react';
function App() {
const [data, setData] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchData() {
try {
const response = await
fetch("https://jsonplaceholder.typicode.com/posts");
if (!response.ok) {
throw new Error("Network response was not ok");
const data = await response.json();
setData(data);
} catch (error) {
setError(error.message);
fetchData();
}, []);
if (error) return <p>Error: {error}</p>;
return (
<div>
<h1>Posts</h1>
<ul>
{data.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
export default App;
4. React Performance Optimization
React.memo
Prevent unnecessary re-renders of components using React.memo:
import React, { useState } from 'react';
const Counter = React.memo(({ count }) => {
console.log('Rendering Counter component');
return <h2>Count: {count}</h2>;
});
function App() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>Increment</button>
<Counter count={count} />
</div>
);
}
export default App;
React.lazy for Code Splitting
Lazy load a component to reduce the bundle size and improve
performance:
import React, { Suspense, lazy } from 'react';
const LazyComponent = lazy(() => import('./LazyComponent'));
function App() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
</div>
);
export default App;
In the above example, LazyComponent will only be loaded when it’s
needed, which helps improve initial load time.
Summary:
1. React Router: Used to manage navigation in your app.
2. Async/Await: Makes it easier to fetch data from an API in React
using async functions.
3. Error Handling: Use try/catch blocks to catch and handle errors in
async functions.
4. Optimization: Use React.memo to prevent unnecessary re-
renders and React.lazy for code splitting to improve performance.