Lecturer: Đỗ Nguyên Kha
Semester 1/2025-2026 @ FIT-HCMUS
propsuseStateuseEffectReact’s declarative and component-based approach offers solutions to the challenges faced when using plain JavaScript for dynamic UIs.
const App = () => {
const handleClick = () => {
alert("Button Clicked!");
};
return (
Welcome to React
);
};
The real DOM is the actual DOM tree that the browser renders and displays to the user. This means any changes made to the real DOM are immediately reflected on the user's screen, but direct manipulation can be inefficient for complex or frequent updates due to the performance cost of re-rendering the UI.
Examine the starting point of our application by viewing the initial HTML structure rendered in the browser. In this step, the initial real DOM rendered by the browser is created.
Hello
React
In this step, React creates a virtual DOM that mirrors the initial real DOM.
const virtualDOM = {
type: 'div',
props: {},
children: [
{
type: 'h1',
props: {},
children: ['Hello'],
},
{
type: 'h2',
props: {},
children: ['React'],
},
],
};
Next, update the UI by adding a new header element:
Hello
React
Element
In this step, React creates an updated virtual DOM that includes the new header element.
const updatedVirtualDOM = {
type: 'div',
props: {},
children: [
{
type: 'h1',
props: {},
children: ['Hello'],
},
{
type: 'h2',
props: {},
children: ['React'],
},
{
type: 'h3',
props: {},
children: ['Element'],
},
],
};
React now compares the initial and updated virtual DOMs to identify what has changed. React uses it's diffing algorithm to determine the differences between the two virtual DOMs by:
After diffing, React determines the specific changes needed. It reviews the findings of the diffing process to see what updates are necessary.
<h1>Hello</h1> and <h2>React</h2><h3>Element</h3>
React updates the real DOM by applying changes identified during the diffing process, inserting the new element (<h3>Element</h3>) into the real DOM under the existing <div> element.
Hello
React
Element
React solves this problem with JSX, a syntax that lets us describe the structure of our UI in a concise, HTML-like format while seamlessly integrating JavaScript logic. It's an optional syntax extension for JavaScript that looks like HTML but is compiled into JavaScript. It allows us to write UI code in a way that’s intuitive and closer to how our UI will look in the browser.
const element = Hello, JSX!
;
const element = React.createElement('h1', null, 'Hello, JSX!');
React solves this problem with JSX, a syntax that lets us describe the structure of our UI in a concise, HTML-like format while seamlessly integrating JavaScript logic.
const items = ['React', 'JSX', 'Components'];
const elements = (
{items.map((item) => (
- {item}
))}
);
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(elements);
createRoot
The createRoot method takes one argument: Where to render - A DOM node in the HTML document where the React content will be displayed.
Once the root container is created, the render method takes: What to render - A React element or JSX expression that describes the UI.
Typically, React applications render their content inside a single HTML container, often an element with the ID root.
createRoot
Even though React components are the preferred way to handle dynamic updates, we can manually call the render method
multiple times to update content. Each call replaces content in the DOM node.
let counter = 0;
const root = ReactDOM.createRoot(document.getElementById('root'));
function updateCounter() {
root.render(Counter: {counter}
);
}
// Initial render
updateCounter();
// Simulate updates
setInterval(() => {
counter++;
updateCounter();
}, 500);
// Greeting component
function Greeting() {
return Hello, World!
;
}
// Footer component
function Footer() {
return © 2025 CSC13008-23KTPM1
;
}
// App
function App() {
return (
<Greeting/>
<Footer/>
);
}
StrictModeReact StrictMode is a development-only tool that helps identify potential problems and enforce best practices in React
applications. It does not affect the UI or production build but enables additional checks and warnings to catch common
bugs and improve code quality early.
props to Componentsprops to Componentsprops (short for “properties”) are a mechanism for passing data from a parent component to a child component. They allow
components to be dynamic and reusable by enabling them to receive input data and render accordingly.
function Message(props) {
return {props.text}
;
}
function App() {
return (
<Message text="Hello, World!" />
<Message text="Welcome to React." />
<Message text="Props are awesome!" />
);
}
props
function Message(props) {
return <p style={{ color: props.color }}>{props.text}</p>;
}
function App() {
return (
<Message text="Hello, World!" color="red" />
<Message text="Welcome to React." color="green" />
<Message text="Props are awesome!" color="blue" />
);
}
UserCard componentIn React, there are three ways to apply styles to our components:
<div className="hello">Using CSS stylesheets </div>
const inlineStyle = {
backgroundColor: '#282c34',
padding: '20px',
color: 'white',
textAlign: 'center',
};
return (
Styling Component
<div style={inlineStyle}>Inline styles</div>
<div style={{ color: 'white' , fontSize: '18px' , backgroundColor: 'navy' , fontWeight: 'bold' }}>Inline styles</div>
);
React handles events similarly to how events are handled in regular HTML and JavaScript, but with some syntactical differences
Use camelCase (e.g., onClick, onChange) and pass a function reference as the event handler, not a string.
<button onClick={handleClick}>Click Me</button>
onClick: Triggered when an element is clicked.onChange: Triggered when the value of an input element changes.onSubmit: Triggered when a form is submitted.onMouseOver: Triggered when the mouse pointer moves over an element.onFocus / onBlur: Triggered when an element gains or loses focus.To pass arguments to our event handlers, wrapping the event handler in an arrow function.
function App() {
function handleClick(message) {
alert(message);
}
return (
<button onClick={() => handleClick('Button clicked!')}>Click Me</button>
);
}
Forms are integral to web applications. React simplifies handling form inputs and submissions.
function App() {
function handleChange(event) {
console.log('Input value:', event.target.value);
}
return (
<input
type="text"
onChange={handleChange}
placeholder="Type something..."
/>
);
}
Sometimes, we may need to prevent the default action of an event, such as preventing a form from submitting and refreshing the page.
function App() {
function handleSubmit(event) {
event.preventDefault();
alert('Form submitted!');
}
return (
<form onSubmit={handleSubmit}>
<input type="text" placeholder="Enter your name" />
</form>
);
}
We can pass event handlers to child components via props, promoting modularity and reusability.
React hooks are special functions that let us use state and other React features without writing class components. The primary goal is to simplify how we manage data (state) and tasks like fetching data, updating the browser title, or running timers (effects) inside the UI components. By using hooks, our functional components can become more powerful while remaining concise and easy to read.

useState hookBy adding useState, the component now maintains its own data and reacts to user actions, transforming from a static,
“read-only” UI into a dynamic, “interactive” one. All this is done without using class components.
function InteractiveGreeting() {
const [message, setMessage] = React.useState("Hello, welcome to our React application!");
function handleClick() {
setMessage("Thanks for clicking, have a great day!");
}
return (
{message}
<button onClick={handleClick}>Click me</button>
);
}
useState hook
useEffect hookThe useEffect hook is used for performing side effects in our components. It runs after the component renders and can
optionally re-run when specific values (called dependencies) change.
React.useEffect(() => {
console.log(`Count updated: ${count}`);
}, [count]); // 'count' is a dependency
useEffect hookDependencies are the values or state variables that, when changed, trigger the re-execution of the effect function
provided to useEffect. These values are listed in an array as the second parameter of useEffect.
Dependencies ensure that the useEffect logic is tied only to the values it depends on, avoiding unnecessary re-runs and optimizing performance. Incorrect dependencies or missing them altogether can lead to bugs like infinite loops or stale values.
useEffect hookThe empty dependency array ([]) ensures the effect runs only after the first render (on mount) and does not re-run
during subsequent renders.
React.useEffect(() => {
console.log("Component mounted");
}, []);
useEffect hookuseEffect hookFetching data is one of the most common side effects in applications.
function UserList() {
const [users, setUsers] = React.useState([]);
const [loading, setLoading] = React.useState(true);
React.useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json())
.then(data => {
setUsers(data);
setLoading(false);
});
}, []);
return (
{loading ? (
Loading...
) : (
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
)}
);
}
useState and useEffect for Dynamic InteractionsIn real-world applications, components often need to manage their internal state and respond to external factors like
user input, fetched data, or timers. By combining useState and useEffect, we can create fully interactive, dynamic
components that manage both state and side effects seamlessly.
/vit/Vite is a build tool that aims to provide a faster and leaner development experience for modern web projects. It consists of two major parts:
npm create vite@latest
TypeScript is JavaScript with syntax for types.
interface User {
name: string;
id: number;
}
useState and useEffect: Add new task, Mark/Unmark as done, Remove taskProp drilling is a situation in React where data (props) is passed from a parent component to deeply nested child components through intermediate components that don’t need the data.
function App() {
const user = { name: 'John Doe', email: '[email protected]' };
return <Parent user={user} />;
}
function Parent({ user }) {
return <Child user={user} />;
}
function Child({ user }) {
return <GrandChild user={user} />;
}
function GrandChild({ user }) {
return User Name: {user.name}
;
}
useContext hookIt allows us to create and provide data across components without prop drilling. It provides a way to pass data down the component tree without manually passing props at every level.
Source: https://dmitripavlutin.com/react-useeffect-explanation/
useContext hook
const UserContext = React.createContext();
function App() {
const user = { name: 'John Doe' };
return (
<UserContext.Provider value={user}>
<GrandChild />
</UserContext.Provider>
);
}
function GrandChild() {
const user = React.useContext(UserContext);
return User Name: {user.name}
;
}
useContext hookLet’s build a theming system where components dynamically adjust their styles (light or dark) based on a shared theme value. By using the useContext hook, we will efficiently share the theme across components without passing props manually.
export const ThemeContext = createContext(); // Create a context
useContext hookConsuming context data with useContext
function ThemedButton() {
const theme = useContext(ThemeContext); // Access the current theme
const buttonStyle = {
backgroundColor: theme === 'dark' ? '#333' : '#fff',
color: theme === 'dark' ? '#fff' : '#333',
padding: '10px 20px',
border: 'none',
cursor: 'pointer',
};
const handleClick = () => {
alert(`Button clicked in ${theme} mode`);
};
return >button style={buttonStyle} onClick={handleClick}<Click me>/button<;
}
export default ThemedButton;
useContext hookOnce the context and the ThemedButton component are ready, we can provide the shared data (theme) to the ThemedButton
using the ThemeContext.Provider.
function App() {
const theme = 'dark'; // Current theme
return (
Welcome to the Themed App!
);
}
const [language, setLanguage] = useState('en'); // Current language
// Manages the language state (en for English, vi for Vietnamese)
const toggleLanguage = () =>
setLanguage((prevLanguage) => (prevLanguage === 'en' ? 'vi' : 'en'));
return (
// Provides the language value to the LanguageContext.Provider
<LanguageContext.Provider value={language}>
<Greeting />
<button onClick={toggleLanguage}>Toggle language</button>
</LanguageContext.Provider>
);
useRef hookThe useRef hook serves multiple purposes in managing data and interacting with the DOM. It provides a way to
create a mutable reference, which can be used for two primary purposes:
useRef hookuseRef value is preserved between renders, making it suitable for
managing non-UI data.useRef value does not affect the rendering cycle of the component.useRef hookuseRef is commonly used to access and manipulate DOM elements directly.
function InputFocus() {
const inputRef = React.useRef(null); // Create a ref object for the second input field
// Handler for focusing the second input field using useRef
const handleFocusWithRef = () => {
inputRef.current.focus(); // Access and focus the input element
};
return (
With useRef
<button onClick={handleFocusWithRef}>Focus input</button>
);
}
useRef hookuseRef is also useful for storing mutable values that need to persist across renders but should not trigger a re-render
when updated: a click counter or a timer.
function ClickCounter() {
const countRef = React.useRef(0); // Mutable value
const [renderCount, setRenderCount] = useState(0);
const incrementCounter = () => {
countRef.current += 1; // Increment the ref value
console.log(`Clicks: ${countRef.current}`);
};
return (
Rendered {renderCount} times
<button onClick={incrementCounter}>Increment counter</button>
<button onClick={() => setRenderCount(renderCount + 1)}>Re-render</button>
);
}
useRef hookuseRef can be used to persist the previous value of a state variable, making it useful for scenarios need to compare the current state with its previous value or perform calculations based on it.
function PreviousValue() {
const [count, setCount] = React.useState(0);
const prevCountRef = React.useRef();
React.useEffect(() => {
prevCountRef.current = count; // Update ref with the current state after rendering
});
return (
Current count: {count}
Previous count: {prevCountRef.current}
<button onClick={() => setCount(count + 1)}>Increment</button>
);
}
Build a timer component that increments a counter every second using useRef to track elapsed time. The timer will have
three buttons to control its behavior: Start, Pause, and Reset. You will use the useRef hook to efficiently track the
timer's interval without causing unnecessary re-renders.
Create a page with multiple sections and a “Scroll to top” button. When clicked, this button will take the user back to the top of the page smoothly.
useMemo hookThe useMemo hook is used to optimize the performance of our application by memoizing the result of a computation. It
returns a memoized value that only recalculates when one of its dependencies changes. This avoids unnecessary
recalculations on every render, making our application more efficient.
useMemo hook
const [count, setCount] = React.useState(0);
// Memoizing the expensive computation
const expensiveCalculation = React.useMemo(() => {
console.log('Running expensive calculation...');
return count * 1000;
}, [count]);
Create a React component that filters a list of programming languages based on user input in a search box. Users will
input a search term in the text box, and your code should dynamically filter and display only the programming languages
that match the search term. Optimize the filtering logic using useMemo.
Create a React component to calculate fibonacci numbers. Users will input a number in the text box, and your code should
compute and display the fibonacci number for that input using an optimized recursive function. Optimize the calculation
using useMemo.
You are tasked with creating an inventory management system that allows users to efficiently manage products and dynamically switch between light and dark themes. The system should include the following features:
quantity* price)
npm install react-hook-form
React applications often require navigation between different pages or views, such as “Home,” “About,” “Projects,” or “Contact” pages. Traditionally, this is done by manually managing state or creating multiple HTML files.
We need to build a portfolio website with the following pages:
const App = () => {
const [currentPage, setCurrentPage] = React.useState('Home');
const renderPage = () => {
switch (currentPage) {
case 'Home':
return Welcome to my portfolio
;
case 'About':
return About me
;
case 'Projects':
return My projects
;
case 'Contact':
return Contact me
;
default:
return 404 page not found
;
}
};
return (
{renderPage()}
);
};
Limitations of manual navigation:
React Router is the de-facto standard for handling routing in React applications. It allows developers to define routes, navigate between views, and manage URL parameters efficiently. With its robust features and integration capabilities, React Router is essential for building modern single-page applications (SPA).
A single-page application (SPA) is a web application that dynamically loads and updates content on a single HTML page. Instead of fetching a new HTML file for each route, SPAs render content dynamically based on the URL. This improves performance and creates a smoother user experience.
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
const Home = () => Welcome to my portfolio
;
const About = () => About me
;
const Projects = () => My projects
;
const Contact = () => Contact me
;
const App = () => (
<Router>
<Routes>
<Route path="/" element={ } />
<Route path="/about" element={ } />
<Route path="/projects" element={ } />
<Route path="/contact" element={ } />
</Routes>
</Router>
);
export default App
Routing is a fundamental feature of single-page React applications that emulate multi-page behavior. It enables developers to map specific URLs to components, allowing for dynamic view rendering and smooth, seamless navigation without full-page reloads.
The Route component is used to map a specific URL path to a React component. The path property specifies the URL, and
the element property determines which component to render when the path matches.
The Routes component acts as a container for multiple Route definitions, ensuring only one matching route is rendered at
a time.
Create a 404 page: Add a wildcard route (*) to render a 404 page for undefined paths.
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
const Home = () => Welcome to my portfolio
;
const About = () => About me
;
const Projects = () => My projects
;
const Contact = () => Contact me
;
const NotFound = () => 404 page not found
;
const App = () => (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/projects" element={<Projects />} />
<Route path="/contact" element={<Contact />} />
{/* A wildcard route (*) to catch all undefined paths */}
<Route path="*" element={<NotFound />} />
</Routes>
</Router>
);
export default App
In traditional HTML, navigation is handled using <a> tags. However, <a> tags cause the browser to perform a full-page reload, which:
React Router provides components like Link and NavLink to enable smooth, single-page navigation without full-page
reloads.
The Link component is used to navigate between routes in a React application. It replaces traditional <a> tags for
internal navigation while maintaining the SPA experience.
While the Link component is great for basic navigation, it doesn’t provide a way to highlight the currently active
route. This is where the NavLink component comes in. It works like Link but includes built-in functionality to detect
the active route and apply specific styles or classes.
NavLink component automatically detects whether the current URL matches the to path and provides an isActive property.isActive property to apply styles or classes dynamically to the active link.Services page to the application and include it in the navigation bar using NavLink. Highlight the active page
when navigating.Link components for the footer
links. The footer should display navigation links similar to the navigation bar.In many real-world applications, URLs often contain dynamic data. For example, a product page URL in an online store
might look like /products/123, where 123 is the product ID that changes for each product available in the store.
In React applications, such scenarios are handled through dynamic routing by defining routes with parameters. Dynamic routing enables us to define routes with placeholders for parameters, allowing URLs to include dynamic values. React Router supports dynamic routing by using a colon (:) in the route path to represent a parameter. For example:
<Route path="/products/:id" element={<ProductDetail />} />
The useParams hook, provided by React Router, allows us to extract parameters from the current URL.
const params = useParams();
It returns an object containing all the dynamic parameters defined in the route as key-value pairs. For example, if the
URL is /products/123 and the route is defined as /products/:id, the object returned by useParams() will look like this:
{ id: "123" }
As applications become more complex and feature-rich, organizing routes in a clear and maintainable way becomes critical. Often, we’ll find that certain sections of an application are logically grouped under a parent route. A great example of this is a "Dashboard" in a web application. Dashboards typically consist of multiple related subsections, such as "Profile", "Settings", and "Notifications", which all fall under the umbrella of the main Dashboard page.
Instead of defining each of these subsections as standalone routes, nested routes allow us to organize them under a
single parent route like /dashboard. This parent-child route structure is useful for applications where some pages or
sections have their own sub-pages. With nested routes, we can build scalable, modular applications while maintaining a
consistent user experience.
A 404 page provides a user-friendly way to inform visitors that the requested page cannot be found. It often includes
helpful navigation options, such as links to the Home page or a search feature, guiding users back to valid content.
React Router allows us to define a wildcard route (path="*") to handle any undefined paths in our application. Wildcard
routes are typically used to display 404 pages.
React Router offers the useNavigate hook to handle programmatic navigations. The useNavigate hook provides a function
that allows us navigate to any route in our application programmatically, without relying on predefined links.
const navigate = useNavigate();
navigate('/path', { replace: true });
Redirecting users after completing a form, such as navigating to a dashboard after signup or login, is a common
requirement in modern applications. The following code demonstrates a simple signup flow where users enter their email
address to proceed. If an email is provided, the user is redirected to the profile page using the useNavigate hook;
otherwise, an alert prompts them to enter an email.
Programmatically navigating to the previous page can improve user experience in scenarios like returning from a details page.