Web Technology 512 Learner Guide
Web Technology 512 Learner Guide
LEARNER GUIDE
PREPARED ON BEHALF OF
RICHFIELD GRADUATE INSTITUTE OF TECHNOLOGY (PTY) LTD
All rights reserved; no part of this publication may be reproduced in any form or by any means, including photocopying
machines, without the written permission of the Institution.
Naik, P.G. and Oza, K.S. (2023) Awesome
[Link]: unleash the power of modern UI
building. International Institute of Organized R
The Diploma in Information Technology (DIT) is a comprehensive and practical program designed
to build a strong foundation in IT principles while equipping students with the hands-on skills
required to meet industry demands. Focused on both theoretical knowledge and applied
learning, this qualification prepares students for intermediate-level roles in IT and serves as a
stepping-stone for further academic progression or specialization. Graduates of this program are
well-prepared to articulate to the Bachelor of Science in IT (BSc IT) qualification. The curriculum
covers programming, networking, database management, system analysis etc., ensuring
graduates possess the competencies to solve real-world IT challenges effectively.
The Bachelor of Science in IT (BSc IT) program is structured to address the growing complexity of
the evolving technological landscape. Through carefully curated modules, students gain a deep
understanding of software development, database management, cloud computing,
cybersecurity, IT management, artificial intelligence, machine learning, networking etc.
Graduates of this program are well-prepared to articulate to the Bachelor of Science Honours in
IT qualification. The curriculum is designed to bridge the gap between academic learning and real-
world applications, thus fostering innovation and an entrepreneurial mindset. Students are
encouraged to participate in research and practical learning.
The programming focus within the IT qualification exemplifies academic innovation and
professional alignment. By integrating a diverse range of programming languages with practical
application, the curriculum prepares students to excel in the rapidly evolving tech industry. The
program aligns with industry courses from globally recognized leading tech giants, such as Oracle,
AWS, IBM, etc ensures that graduates possess the credentials to validate their expertise in
software development and cloud-based technologies. This blend of foundational knowledge,
practical experience, and industry-standard courses prepares students for immediate
employment and establishes a strong basis for long-term career advancement in software
development.
Web Technology 512 builds on foundational web development skills by introducing students to
advanced JavaScript concepts and the modern [Link] framework. Students learn to develop
dynamic, responsive, and component-based user interfaces while mastering key techniques such
as state management, event handling, and lifecycle methods. The module emphasizes practical,
real-world development practices, enabling students to build scalable single-page applications
(SPAs) using React. By the end of the course, students are equipped with the skills to create
interactive and efficient web applications, preparing them for advanced roles in frontend and full-
stack development.
1 Fundamental JavaScript for React
Learning Objective
1.1 Introduction
JavaScript has evolved significantly since its creation in 1995. The introduction of ECMAScript
2015 (ES6) marked a major milestone in the language's evolution, bringing features that made
JavaScript more powerful and easier to work with. React, being a modern JavaScript library,
heavily relies on these ES6+ features. Understanding these fundamentals is crucial before
embarking on React development.
This chapter provides a theoretical foundation of JavaScript concepts that are essential for React
development. We will explore the language features, their purposes, and their significance in
modern web development. While the focus is on understanding concepts, we'll include practical
examples to illustrate how these features work in real code. The knowledge gained here will serve
as the building blocks for understanding React's component-based architecture in subsequent
chapters.
1.2 Evolution of JavaScript and ES6+
JavaScript was created by Brendan Eich in just 10 days in 1995. Originally named Mocha, then
LiveScript, and finally JavaScript, the language was designed to add interactivity to web pages.
Despite its humble beginnings and initial limitations, JavaScript has grown to become one of the
most widely used programming languages in the world. The ECMAScript specification, first
published in 1997, standardized the language and provided a foundation for its evolution.
The early years of JavaScript were marked by browser incompatibilities and limited features.
Developers often had to write different code for different browsers, and the language lacked
many features that programmers took for granted in other languages. This situation persisted for
many years, with only minor updates to the language specification.
ECMAScript 2015, commonly known as ES6, represented the most significant update to
JavaScript since its creation. This update addressed many of the language's historical pain points
and introduced features that made JavaScript more suitable for large-scale application
development. The introduction of block-scoped variables through let and const keywords solved
issues with variable scoping that had plagued developers for years. Arrow functions provided a
more concise function syntax while also solving problems with the this keyword binding. Classes
brought a more familiar syntax for object-oriented programming, even though they were
syntactic sugar over JavaScript's prototype-based inheritance.
The module system introduced in ES6 finally gave JavaScript a standardized way to organize code
across multiple files, addressing the global namespace pollution that had been a significant issue
in large applications. Promises provided a better way to handle asynchronous operations, moving
away from the callback-heavy patterns that often led to deeply nested and hard-to-read code.
Destructuring assignment made it easier to extract data from arrays and objects, while template
literals solved the cumbersome string concatenation problems that developers frequently
encountered.
Global scope refers to variables that are accessible throughout your entire program. These
variables are declared outside any function or block and can be accessed from anywhere in your
code. While global variables can be convenient, they should be used sparingly as they can lead
to naming conflicts and make code harder to maintain and debug.
Function scope encompasses variables declared inside a function. These variables are only
accessible within that function and any nested functions inside it. This has been a fundamental
feature of JavaScript since its inception and provides a way to encapsulate variables and prevent
them from polluting the global namespace.
Block scope, introduced with ES6, refers to variables that are only accessible within the block in
which they are declared. A block is defined by curly braces, such as those used in if statements,
for loops, and other control structures. This addition brought JavaScript in line with many other
programming languages and solved numerous scoping issues that developers had struggled with.
The traditional var keyword, which was the only way to declare variables in JavaScript before ES6,
has several characteristics that can lead to confusion and bugs.
Consider this example:
function demonstrateVar() {
[Link](x); // undefined (not an error!)
var x = 5;
if (true) {
var y = 10;
}
[Link](y); // 10 (accessible outside the block)
}
Variables declared with var are function-scoped or globally-scoped, meaning they ignore block
boundaries. They can also be re-declared within the same scope without causing an error, which
can lead to accidentally overwriting variables. Perhaps most confusingly, var declarations are
subject to hoisting, which means they are conceptually moved to the top of their containing
function or global scope, though only the declaration is hoisted, not the initialization.
The let keyword, introduced in ES6, addresses many of the issues with var. Here's how let
behaves differently:
function demonstrateLet() {
// [Link](x); // ReferenceError: Cannot access 'x' before initialization
let x = 5;
if (true) {
let y = 10;
[Link](y); // 10
}
// [Link](y); // ReferenceError: y is not defined
}
Variables declared with let are block-scoped, meaning they respect the boundaries of blocks
defined by curly braces. They cannot be re-declared within the same scope, which helps prevent
accidental variable overwrites. While let declarations are technically hoisted, they remain in a
"temporal dead zone" from the start of the block until the declaration is encountered, meaning
accessing them before declaration results in a ReferenceError rather than undefined.
The const keyword shares the block-scoping behavior of let but adds an additional constraint:
const PI = 3.14159;
// PI = 3.14; // TypeError: Assignment to constant variable
const user = { name: 'John', age: 30 };
[Link] = 31; // This is allowed - the object is mutable
// user = { name: 'Jane' }; // TypeError: Assignment to constant variable
The immutability only applies to the binding itself. Variables declared with const must be
initialized at the time of declaration, and attempting to declare a const without an initial value
will result in a syntax error.
1.3.3 Hoisting
Hoisting is one of JavaScript's most misunderstood features. It refers to the behavior where
variable and function declarations are conceptually moved to the top of their containing scope
during the compilation phase. However, it's important to understand that only the declarations
are hoisted, not the initializations.
Function declarations are fully hoisted, meaning both the declaration and the function body are
available throughout their containing scope. This allows functions to be called before they appear
in the code, which can be convenient but can also lead to confusion about the order of execution.
Variable declarations with var are hoisted, but their initialization remains at the original location
in the code. This means that accessing a var variable before its initialization line will return
undefined rather than causing an error. This behavior has been the source of many subtle bugs
in JavaScript programs.
The temporal dead zone for let and const declarations represents a significant improvement in
JavaScript's hoisting behavior. While these declarations are technically hoisted, accessing them
before their declaration results in a ReferenceError. This makes the code more predictable and
helps catch errors earlier in the development process.
Functions are first-class citizens in JavaScript, meaning they can be assigned to variables, passed
as arguments to other functions, and returned from functions. This flexibility has made JavaScript
particularly well-suited for functional programming patterns and event-driven programming.
Function declarations use the function keyword followed by a name, parameters in parentheses,
and a function body in curly braces:
The choice between function declarations and expressions often comes down to the specific use
case and coding style preferences. Function declarations are often preferred for main functions
that represent key behaviors in a module, while function expressions are commonly used for
callbacks, event handlers, and functions that are passed as arguments.
Arrow functions, introduced in ES6, represent more than just a syntactic shorthand for writing
functions. They fundamentally change how the this keyword behaves and remove several
features of traditional functions that were rarely used and often caused confusion. Here's a
comparison:
// Traditional function
const traditional = function(x) {
return x * 2;
};
// Arrow function - concise syntax
const arrow = x => x * 2;
// Arrow function with multiple parameters
const add = (a, b) => a + b;
// Arrow function with block body
const complexCalc = (x, y) => {
const sum = x + y;
const product = x * y;
return sum + product;
};
The most visible benefit of arrow functions is their concise syntax. For simple functions that
return a single expression, arrow functions allow you to omit the function keyword, the curly
braces, and the return statement. This conciseness is particularly valuable when writing callbacks
for array methods or event handlers.
However, the most significant feature of arrow functions is their lexical this binding. In traditional
functions, the value of this depends on how the function is called. Arrow functions, on the other
hand, inherit this from their enclosing scope:
const obj = {
name: 'MyObject',
traditionalMethod: function() {
setTimeout(function() {
[Link]([Link]); // undefined - 'this' refers to global/window
}, 1000);
},
arrowMethod: function() {
setTimeout(() => {
[Link]([Link]); // 'MyObject' - 'this' is inherited
}, 1000);
}
};
Arrow functions also lack their own arguments object, cannot be used as constructors with the
new keyword, and don't have a prototype property. These omissions might seem limiting, but
they actually encourage cleaner, more predictable code.
1.5 De-structuring
De-structuring is a powerful feature introduced in ES6 that allows you to extract values from
arrays or properties from objects and assign them to variables in a more concise and readable
way. This feature has become particularly important in React development, where it's commonly
used to extract props and state values.
Before de-structuring, extracting multiple values from an object or array required multiple
statements, each accessing the structure and assigning to a variable. This approach was verbose
and became particularly cumbersome when dealing with nested structures. De-structuring solves
this problem by allowing you to unpack values using a syntax that mirrors the structure of the
data.
The power of de-structuring extends beyond simple convenience. It makes function parameters
more self-documenting by clearly showing what properties of an object argument are being used.
It also enables functions to effectively return multiple values by returning an object or array that
can be immediately de-structured by the caller.
Object de-structuring uses curly braces on the left side of an assignment to extract properties
from an object. Here's how it works in practice:
One of the most useful features of object de-structuring is the ability to provide default values. If
a property doesn't exist in the object or its value is undefined, the default value will be used
instead. This feature helps make code more robust by gracefully handling missing data.
Array de-structuring works similarly to object de-structuring but uses square brackets instead of
curly braces:
The key difference is that array de-structuring is based on position rather than property names.
The first variable gets the first element, the second variable gets the second element, and so on.
You can skip elements in an array by leaving empty spaces between commas.
The spread operator, denoted by three dots (...), is one of the most versatile features introduced
in ES6. When used with arrays, it expands the array into individual elements:
// Array spreading
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
// Combining arrays
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]
// Copying arrays (shallow copy)
const copy = [...arr1]; // [1, 2, 3]
// Adding elements
const withExtra = [0, ...arr1, 4]; // [0, 1, 2, 3, 4]
// Converting iterables to arrays
const letters = [..."hello"]; // ['h', 'e', 'l', 'l', 'o']
The spread operator also works with objects, though this feature was added in ES2018 rather
than ES6:
// Object spreading
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
// Merging objects
const merged = { ...obj1, ...obj2 }; // { a: 1, b: 2, c: 3, d: 4 }
// Copying objects (shallow copy)
const copy = { ...obj1 }; // { a: 1, b: 2 }
// Overriding properties
const updated = { ...obj1, b: 3 }; // { a: 1, b: 3 }
// Adding properties
const extended = { ...obj1, c: 3 }; // { a: 1, b: 2, c: 3 }
In function calls, the spread operator can expand an array into individual arguments:
Rest parameters use the same three-dot syntax as the spread operator but serve the opposite
purpose. While spread expands elements, rest parameters collect multiple elements into an
array:
Rest parameters provide a cleaner alternative to the arguments object that has been available in
JavaScript functions since the beginning. Unlike arguments, which is an array-like object, rest
parameters create a real array with all array methods available.
1.7 Template Literals
Template literals, also called template strings, revolutionized string handling in JavaScript. Before
their introduction, creating strings with dynamic content required concatenation with the plus
operator, which quickly became unwieldy with complex strings.
Template literals are enclosed in backticks rather than single or double quotes. This simple
change enables several powerful features:
In React development, template literals are frequently used for creating dynamic class names
based on component state or props. They make it easy to conditionally include CSS classes or
build class strings from multiple sources. The readability improvement over concatenation is
particularly valuable in JSX, where clarity is essential.
Template literals also excel at constructing URLs with query parameters or path segments. The
ability to embed expressions directly makes it clear what parts of the URL are dynamic and
reduces the chance of errors from incorrect concatenation.
JavaScript's type system is often misunderstood, partly because it differs significantly from
statically typed languages. JavaScript has seven primitive types, each serving a specific purpose
in the language. Let's explore them with examples:
The Number type represents both integers and floating-point numbers using double-precision
64-bit format. This can sometimes lead to precision issues with decimal arithmetic, which is
important to understand when working with financial calculations or other precision-sensitive
operations.
Strings in JavaScript are immutable, meaning once created, a string cannot be modified.
Operations that appear to modify strings actually create new strings. This immutability has
performance implications and affects how you should think about string manipulation in your
programs.
1.8.2 Object Types
In JavaScript, everything that isn't a primitive is an object. This includes obvious objects like plain
objects created with curly braces, but also arrays, functions, dates, regular expressions, and
more:
// Plain object
const person = {
name: 'John',
age: 30,
greet() {
return `Hello, I'm ${[Link]}`;
}
};
// Array (special type of object)
const numbers = [1, 2, 3, 4, 5];
[Link](typeof numbers); // 'object'
[Link]([Link](numbers)); // true
// Function (also an object!)
function add(a, b) {
return a + b;
}
[Link] = 'Adds two numbers'; // Functions can have properties
[Link]([Link]); // 'Adds two numbers'
// Date object
const now = new Date();
// Map and Set (ES6 collections)
const map = new Map();
[Link]('key', 'value');
const set = new Set([1, 2, 3, 3, 3]); // Set stores unique values
[Link]([...set]); // [1, 2, 3]
Understanding that these are all objects helps explain why they share certain behaviors, like the
ability to have properties and methods. Arrays are ordered lists of values, but being objects, they
can also have non-numeric properties. However, using arrays as general objects is discouraged
as it can lead to confusing code.
JavaScript's dynamic typing means that values are automatically converted between types as
needed. This type coercion can be either implicit or explicit:
// Implicit coercion
[Link]('5' + 3); // '53' (number coerced to string)
[Link]('5' - 3); // 2 (string coerced to number)
[Link](true + 1); // 2 (true coerced to 1)
// Explicit coercion
[Link](Number('5')); // 5
[Link](String(123)); // '123'
[Link](Boolean(0)); // false
// Truthy and falsy values
// Falsy values: false, 0, '', null, undefined, NaN
// Everything else is truthy
if ('') {
[Link]('This won\'t run'); // Empty string is falsy
}
if ('hello') {
[Link]('This will run'); // Non-empty string is truthy
}
// Common pattern using logical operators
const name = '' || 'Default Name'; // 'Default Name'
const count = 0 || 10; // 10 (might not be what you want!)
const actualCount = 0 ?? 10; // 0 (nullish coalescing - only null/undefined)
The concept of truthy and falsy values is central to JavaScript's type coercion. In boolean contexts,
values are coerced to either true or false. Only six values are falsy: false, 0, empty string, null,
undefined, and NaN. All other values, including empty arrays and objects, are truthy.
The principle of immutability means not modifying existing data but instead creating new data
structures with the desired changes. This approach makes programs more predictable and easier
to debug, as you don't have to track how data changes over time. React's emphasis on
immutability for state updates makes understanding these concepts essential.
Let's explore the most important array methods with practical examples:
// More complex reduce example const usersByAge = [Link]((groups, user) => { const
ageGroup = [Link] < 30 ? 'young' : 'adult'; groups[ageGroup] = groups[ageGroup] || [];
groups[ageGroup].push(user); return groups; }, {});
// find() and findIndex() const user = [Link](u => [Link] === 'Bob');
[Link](user); // { id: 2, name: 'Bob', age: 30 }
const index = [Link](u => [Link] === 'Bob');
[Link](index); // 1
// Chaining methods const result = numbers .filter(n => n > 2) .map(n => n * 2) .reduce((sum, n)
=> sum + n, 0);
[Link](result); // 24 (32 + 42 + 5*2)
The map() method creates a new array by applying a transformation function to each element of
the original array. The original array remains unchanged, embodying the principle of immutability.
The filter() method creates a new array containing only elements that pass a test implemented
by the provided function.
The reduce() method is the most powerful and flexible of the array methods. It reduces an array
to a single value by repeatedly applying a function that combines the accumulated result with
each element. While often used for summing numbers, reduce() can build any type of value,
including objects and arrays.
Before ES6 modules, JavaScript lacked a built-in module system, leading to various community
solutions and significant challenges in large-scale application development. The global
namespace became polluted with variables and functions from different scripts, leading to
naming conflicts and making it difficult to track dependencies.
The ES6 module system addresses these issues with a standardized approach to organizing and
sharing code. Here's how modules work in practice:
Named exports allow a module to export multiple values, each with a specific name. These
exports can be functions, classes, variables, or any other JavaScript value. When importing, you
must use the same names or explicitly rename them. Default exports provide a way to export a
single main value from a module. This is useful when a module has one primary purpose.
Re-exports allow a module to export values from another module, effectively passing them
through. This is useful for creating "barrel" modules that aggregate and re-export related
functionality from multiple modules, providing a single convenient import point for consumers.
The call stack executes synchronous code. When a function is called, it's added to the stack, and
when it returns, it's removed. JavaScript can only execute one piece of code at a time, processing
whatever is at the top of the stack. The task queue holds callbacks from asynchronous operations
like setTimeout, I/O operations, and events. When the call stack is empty, the event loop takes
the first task from the queue and pushes it onto the stack for execution.
1.11.2 Callbacks, Promises, and Async/Await
Callbacks were JavaScript's original mechanism for handling asynchronous operations. While
conceptually simple, callbacks can lead to deeply nested code when multiple asynchronous
operations need to be coordinated, a problem known as "callback hell."
Good code organization is essential for maintaining large React applications. Using meaningful
and consistent naming conventions helps developers quickly understand what variables and
functions do. Functions should be small and focused on a single responsibility, making them
easier to test and reuse. Related functionality should be grouped together, whether in the same
file or in logically organized directories.
The principle of least surprise suggests that code should behave in ways that developers expect.
This means following established patterns and conventions within the React ecosystem and being
consistent throughout your application. When you must deviate from common patterns, clear
documentation explaining why helps future maintainers understand your decisions.
Robust error handling prevents applications from crashing and provides better user experiences.
JavaScript's try/catch blocks allow you to handle synchronous errors, while promise rejections
and async/await require different strategies:
Understanding these different error handling mechanisms is essential for building reliable
applications. In production, error boundaries (a React feature that leverages JavaScript's error
handling) prevent entire applications from crashing due to component errors.
1.13 Summary
This chapter has provided a comprehensive foundation of JavaScript concepts essential for React
development. We began by exploring the evolution of JavaScript and the revolutionary changes
introduced in ES6, understanding why these changes were necessary for modern web
development.
We examined modern variable declarations and scope management, understanding how let and
const address the historical issues with var. The exploration of functions covered both traditional
and arrow functions, with particular attention to how arrow functions' lexical this binding solves
common problems in React components.
De-structuring and the spread operator were revealed as powerful features for working with data
structures, while template literals showed how modern JavaScript makes string handling more
elegant. We delved into JavaScript's type system, understanding both primitive and object types
and how type coercion affects our code.
The functional programming features of JavaScript, particularly array methods, were shown to
align perfectly with React's declarative paradigm. The module system's importance in organizing
large-scale applications was explored, along with the patterns for importing and exporting code.
Finally, we touched on asynchronous JavaScript concepts and best practices that will be crucial
when building React applications. Throughout the chapter, we've included practical examples to
illustrate how these concepts work in real code, preparing you for the hands-on work ahead.
These concepts form the backbone of modern JavaScript development and are prerequisites for
understanding React's component-based architecture. In the following chapters, we will see how
these JavaScript features are applied in React development, building upon this theoretical and
practical foundation to create powerful, maintainable web applications.
1.14 Review Questions
Learning Objectives
• Understand what React is and why it has become a dominant library for building user interfaces,
• Set up a React development environment using [Link] and create-react-app,
• Create and understand functional components as the building blocks of React applications,
• Work with props to pass data between components,
• Implement basic state management using the useState hook,
• Build simple interactive React applications that respond to user input.
2.1 Introduction
React has revolutionized the way we build user interfaces for web applications. Created by
Facebook (now Meta) in 2013, React has grown from an internal project to one of the most
popular JavaScript libraries in the world. This chapter introduces you to React's fundamental
concepts and provides hands-on experience creating your first React components.
Understanding React requires a shift in thinking about how web applications are built. Instead of
manipulating the DOM directly, React introduces a declarative approach where you describe
what the UI should look like at any given state, and React efficiently updates the DOM to match
that description. This mental model, combined with React's component-based architecture,
makes building complex user interfaces more manageable and maintainable.
2.2 What is React?
React is a JavaScript library for building user interfaces, particularly web applications that require
frequent data changes. Unlike full frameworks that provide everything needed to build an
application, React focuses specifically on the view layer, giving developers the freedom to choose
other libraries for different aspects of their application.
At its core, React embraces several key principles that set it apart from traditional web
development approaches. The first is the concept of declarative programming. Instead of
imperatively telling the browser how to update the page step by step, you declare what the page
should look like based on the current application state. React then figures out the most efficient
way to update the DOM to match your declaration.
The second principle is component-based architecture. React applications are built using
components, which are self-contained pieces of UI that manage their own state and rendering
logic. Components can be composed together like building blocks, allowing developers to build
complex UIs from simple, reusable pieces. This modular approach promotes code reusability,
makes testing easier, and helps teams collaborate more effectively.
The third principle is the unidirectional data flow. Data in React applications flows in one direction,
from parent components to child components through props. This predictable data flow makes
applications easier to debug and understand, as you can trace where data comes from and how
it affects the UI.
One of React's most innovative features is the Virtual DOM, a lightweight JavaScript
representation of the actual DOM. When you write React code, you're not directly manipulating
the browser's DOM. Instead, you're working with React's Virtual DOM, which acts as an
intermediary layer.
The Virtual DOM solves a fundamental performance problem in web development. Direct DOM
manipulation is expensive because the browser must recalculate layouts, repaint elements, and
perform other costly operations. When you have an application with frequent updates, these
operations can make the UI feel sluggish and unresponsive.
React's Virtual DOM works by maintaining a virtual representation of your UI in memory. When
the state of your application changes, React creates a new virtual DOM tree representing the
new state. It then compares this new tree with the previous virtual DOM tree, a process called
"diffing." Through this comparison, React identifies exactly what has changed and updates only
those specific parts of the real DOM. This selective updating, called "reconciliation," dramatically
improves performance, especially in applications with complex UIs and frequent updates.
React has become popular for several compelling reasons. First, its component-based
architecture aligns well with modern software development practices. Components encapsulate
both logic and presentation, making them easy to understand, test, and reuse. This modularity is
particularly valuable in large applications where different teams might work on different parts of
the UI.
Second, React's ecosystem is vast and mature. The community has created thousands of libraries
and tools that work with React, from state management solutions to UI component libraries. This
ecosystem means you rarely have to build common functionality from scratch, accelerating
development time.
Third, React's learning curve, while initially steep, plateaus relatively quickly. Once you
understand the core concepts of components, props, and state, you can build increasingly
complex applications using the same patterns. This consistency makes React knowledge
transferable across projects and teams.
Finally, React's popularity means excellent job prospects for developers. Many companies use
React for their web applications, creating a strong demand for React developers. Learning React
opens doors to numerous career opportunities in web development.
Before we can start building React applications, we need to set up our development environment.
React development requires [Link], a JavaScript runtime that allows us to run JavaScript outside
the browser. [Link] comes with npm (Node Package Manager), which we'll use to install React
and other dependencies.
To install [Link], visit the official [Link] website ([Link]) and download the installer for
your operating system. For this course, we recommend using the LTS (Long Term Support) version,
which provides stability and long-term maintenance. During installation, make sure to include
npm, which is typically selected by default.
After installation, you can verify that [Link] and npm are properly installed by opening a
terminal or command prompt and running the following commands:
node --version
npm –version
These commands should display version numbers, confirming successful installation. If you see
version numbers (for example, node v18.17.0 and npm 9.6.7), you're ready to proceed. If not,
you may need to restart your terminal or check your system's PATH environment variable.
React provides an official tool called Create React App that sets up a new React project with a
modern build configuration. This tool handles complex webpack configuration, Babel setup, and
development server, allowing you to focus on writing React code rather than configuring build
tools.
To create a new React application, open your terminal and navigate to the directory where you
want to create your project. Then run:
The npx command runs the latest version of create-react-app without globally installing it. This
process might take a few minutes as it downloads dependencies and sets up your project
structure. Once complete, the npm start command launches a development server and opens
your new React application in your default web browser at [Link]
Create React App generates a project structure that follows React best practices. Let's explore
the key files and directories:
The node_modules directory contains all the project dependencies. This folder is typically large
and should not be committed to version control. The public directory contains static files that
don't go through the build process. The [Link] file is particularly important as it's the single
HTML page that hosts your React application.
The src directory is where you'll spend most of your time. It contains all your React components
and associated files. The [Link] file is the entry point of your application, where React is
initialized and the root component is rendered. The [Link] file contains the main App component,
which serves as the starting point for your component tree.
The [Link] file lists your project dependencies and scripts. You can run various commands
defined in this file, such as npm start for development, npm test for testing, and npm run build
for creating a production build.
Components are the fundamental building blocks of React applications. A component is a self-
contained piece of UI that encapsulates its own structure, style, and behavior. Think of
components as custom HTML elements that you can create and reuse throughout your
application.
In traditional web development, you might have a large HTML file with JavaScript manipulating
various parts of it. This approach becomes unwieldy as applications grow. React's component-
based approach solves this by breaking the UI into independent, reusable pieces. Each
component manages its own piece of the UI, making the overall application easier to understand
and maintain.
Components can be as simple as a button or as complex as an entire form or page. The key is that
each component has a single, well-defined purpose. This single responsibility principle makes
components easier to understand, test, and reuse. When building React applications, you'll often
start with larger components and then break them down into smaller, more focused components
as patterns emerge.
2.4.2 Creating Your First Functional Component
Let's create our first React component. In React, components are JavaScript functions that return
JSX (JavaScript XML), a syntax extension that looks like HTML but is actually JavaScript. Here's a
simple component:
This Welcome component is a function that returns JSX describing what should appear on the
screen. The JSX looks like HTML, but it's actually JavaScript code that React transforms into
function calls that create React elements. Notice how we export the component so it can be
imported and used in other files.
React has specific conventions for components that help maintain consistency across projects.
Component names must start with a capital letter to distinguish them from regular HTML
elements. When React sees an element starting with a lowercase letter, it treats it as a DOM tag.
When it starts with a capital letter, React treats it as a component.
Component files are typically organized in a components directory within the src folder. Each
component usually lives in its own file, with the filename matching the component name. This
organization makes it easy to find and maintain components as your application grows.
// src/components/[Link]
function Header() {
return (
<header>
<h1>My React Application</h1>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
</header>
);
}
export default Header;
// src/components/[Link]
function Footer() {
const currentYear = new Date().getFullYear();
return (
<footer>
<p>© {currentYear} My React Application. All rights reserved.</p>
</footer>
);
}
export default Footer;
// src/[Link]
import Header from './components/Header';
import Footer from './components/Footer';
import Welcome from './components/Welcome';
function App() {
return (
<div className="App">
<Header />
<main>
<Welcome />
</main>
<Footer />
</div>
);
}
export default App;
This example demonstrates how components can be composed together to create a complete
page layout. Each component has a specific responsibility: Header for navigation, Footer for
copyright information, and Welcome for the main content.
Props (short for properties) are how we pass data from parent components to child components
in React. They're similar to function arguments or HTML attributes. Props make components
flexible and reusable by allowing them to display different content based on the values passed
to them.
Props are read-only, meaning a component cannot modify its own props. This immutability is a
key principle in React that helps maintain predictable data flow. When a parent component
passes props to a child, the child can use those props to render different content or behave
differently, but it cannot change the prop values themselves.
Think of props as a component's configuration. Just as HTML elements have attributes that affect
their behavior and appearance, React components have props that customize their output. This
system allows you to create generic components that can be specialized for different use cases.
// src/components/[Link]
function Welcome(props) {
return (
<div>
<h1>Welcome to React, {[Link]}!</h1>
<p>You are {[Link]} years old.</p>
<p>{[Link]}</p>
</div>
);
}
export default Welcome;
// src/[Link]
import Welcome from './components/Welcome';
function App() {
return (
<div className="App">
<Welcome
name="Alice"
age={25}
message="Hope you enjoy learning React!"
/>
<Welcome
name="Bob"
age={30}
message="React makes building UIs fun!"
/>
</div>
);
}
export default App;
Notice how we pass props to the Welcome component using syntax similar to HTML attributes.
String props are passed in quotes, while numeric values and other JavaScript expressions are
passed in curly braces. The component receives all props as properties of a single props object.
De-structuring props directly in the function parameter makes the code more readable and
shows clearly which props the component expects. This pattern is widely used in React
development.
While JavaScript is dynamically typed, it's helpful to document what props a component expects.
We can provide default values for props to make components more robust:
// src/components/[Link]
function UserCard({ name = "Guest", role = "Visitor", isActive = false }) { return (
{name}
Role: {role}
Status: {isActive ? "Active" : "Inactive"}
); }
export default UserCard;
// src/[Link] import UserCard from './components/UserCard';
function App() { return (
{/* Uses all default values */}
); }
Default values ensure that components work even when some props are not provided, making
them more flexible and preventing errors from undefined values.
While props allow parent components to pass data to children, state allows components to
manage their own data that can change over time. State is what makes React applications
interactive and dynamic. When state changes, React automatically re-renders the component to
reflect the new state.
State represents any data that can change during the component's lifetime. This might include
user input, data fetched from an API, timers, or any other dynamic values. The key difference
between props and state is that props are passed from parent to child and are immutable within
the component, while state is managed internally by the component and can be changed.
In functional components, we manage state using the useState hook, one of React's built-in hooks.
Hooks are special functions that allow functional components to use React features that were
previously only available in class components.
The useState hook is the most fundamental hook in React. It allows functional components to
have state. Here's how to use it:
// src/components/[Link]
import { useState } from 'react';
function Counter() {
// Declare state variable 'count' with initial value 0
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
const decrement = () => {
setCount(count - 1);
};
const reset = () => {
setCount(0);
};
return (
<div>
<h2>Counter: {count}</h2>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
<button onClick={reset}>Reset</button>
</div>
);
}
export default Counter;
The useState hook returns an array with two elements: the current state value and a function to
update it. We use array destructuring to assign these to variables. The convention is to name the
setter function with "set" followed by the state variable name.
When you call the setter function (like setCount), React schedules a re-render of the component
with the new state value. This re-render happens asynchronously and efficiently updates only
the parts of the DOM that have changed.
State becomes particularly useful when handling user input. Let's create a component that
responds to user typing:
// src/components/[Link]
import { useState } from 'react';
function Greeting() {
const [name, setName] = useState('');
const [submitted, setSubmitted] = useState(false);
const handleInputChange = (event) => {
setName([Link]);
setSubmitted(false);
};
const handleSubmit = (event) => {
[Link]();
setSubmitted(true);
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={handleInputChange}
placeholder="Enter your name"
/>
<button type="submit">Submit</button>
</form>
{name && !submitted && (
<p>Hello, {name}! Click submit when ready.</p>
)}
{submitted && name && (
<h2>Welcome to React, {name}!</h2>
)}
</div>
);
}
export default Greeting;
This example demonstrates several important concepts. First, we use controlled components
where the input's value is controlled by React state. The value prop of the input is set to our state
variable, and the onChange handler updates the state when the user types.
Second, we handle form submission by preventing the default browser behavior and updating
our component state. This pattern of controlling form inputs through React state is fundamental
to handling user input in React applications.
Components often need to track multiple pieces of state. You can call useState multiple times in
a single component:
// src/components/[Link]
import { useState } from 'react';
function UserForm() {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [email, setEmail] = useState('');
const [submitted, setSubmitted] = useState(false);
const handleSubmit = (event) => {
[Link]();
setSubmitted(true);
};
const handleReset = () => {
setFirstName('');
setLastName('');
setEmail('');
setSubmitted(false);
};
return (
<div>
{!submitted ? (
<form onSubmit={handleSubmit}>
<div>
<input
type="text"
value={firstName}
onChange={(e) => setFirstName([Link])}
placeholder="First Name"
required
/>
</div>
<div>
<input
type="text"
value={lastName}
onChange={(e) => setLastName([Link])}
placeholder="Last Name"
required
/>
</div>
<div>
<input
type="email"
value={email}
onChange={(e) => setEmail([Link])}
placeholder="Email"
required
/>
</div>
<button type="submit">Submit</button>
</form>
):(
<div>
<h3>Submitted Information:</h3>
<p>Name: {firstName} {lastName}</p>
<p>Email: {email}</p>
<button onClick={handleReset}>Submit Another</button>
</div>
)}
</div>
);
}
export default UserForm;
Each piece of state is independent, and updating one doesn't affect the others. This granular
control over state makes it easy to manage complex component behavior.
Real-world components often use both props and state. Props provide configuration from parent
components, while state manages internal component data. Let's create a more complex
example that demonstrates this combination:
// src/components/[Link]
import { useState } from 'react';
function TodoItem({ task, onDelete }) {
const [isCompleted, setIsCompleted] = useState(false);
const handleToggle = () => {
setIsCompleted(!isCompleted);
};
const textStyle = {
textDecoration: isCompleted ? 'line-through' : 'none',
color: isCompleted ? '#888' : '#000'
};
return (
<div className="todo-item">
<input
type="checkbox"
checked={isCompleted}
onChange={handleToggle}
/>
<span style={textStyle}>{task}</span>
<button onClick={() => onDelete(task)}>Delete</button>
</div>
);
}
export default TodoItem;
// src/components/[Link]
import { useState } from 'react';
import TodoItem from './TodoItem';
function TodoList() {
const [tasks, setTasks] = useState([]);
const [inputValue, setInputValue] = useState('');
const addTask = (event) => {
[Link]();
if ([Link]()) {
setTasks([...tasks, inputValue]);
setInputValue('');
}
};
const deleteTask = (taskToDelete) => {
setTasks([Link](task => task !== taskToDelete));
};
return (
<div className="todo-list">
<h2>My Todo List</h2>
<form onSubmit={addTask}>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue([Link])}
placeholder="Add a new task"
/>
<button type="submit">Add</button>
</form>
<div className="tasks">
{[Link]((task, index) => (
<TodoItem
key={index}
task={task}
onDelete={deleteTask}
/>
))}
</div>
{[Link] === 0 && (
<p>No tasks yet. Add one above!</p>
)}
</div>
);
}
export default TodoList;
This example shows several important patterns. The TodoList component manages the list of
tasks in its state and passes individual tasks as props to TodoItem components. Each TodoItem
manages its own completed state internally. The parent component also passes a callback
function (onDelete) that child components can call to communicate back to the parent.
Example Output in the browser (initial state):
React's event handling system is similar to HTML's but with some important differences. React
events are named using camelCase rather than lowercase, and you pass a function as the event
handler rather than a string. React's synthetic event system ensures consistent behavior across
different browsers.
// src/components/[Link]
import { useState } from 'react';
function EventDemo() {
const [message, setMessage] = useState('');
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
const [inputFocused, setInputFocused] = useState(false);
const handleClick = () => {
setMessage('Button clicked!');
};
const handleMouseMove = (event) => {
setMousePosition({
x: [Link],
y: [Link]
});
};
const handleFocus = () => {
setInputFocused(true);
setMessage('Input focused');
};
const handleBlur = () => {
setInputFocused(false);
setMessage('Input lost focus');
};
return (
<div className="event-demo" onMouseMove={handleMouseMove}>
<h3>Event Handling Demo</h3>
<button onClick={handleClick}>Click Me</button>
<input
type="text"
onFocus={handleFocus}
onBlur={handleBlur}
style={{
border: inputFocused ? '2px solid blue' : '1px solid gray'
}}
placeholder="Click to focus"
/>
<p>Message: {message}</p>
<p>Mouse Position: X: {mousePosition.x}, Y: {mousePosition.y}</p>
</div>
);
}
export default EventDemo;
When handling events in React, it's important to understand that event handlers receive a
synthetic event object that wraps the native browser event. This provides consistent properties
and methods across different browsers.
Let's combine everything we've learned to build a simple but complete interactive application -
a color picker:
// src/components/[Link]
import { useState } from 'react';
function ColorPicker() {
const [selectedColor, setSelectedColor] = useState('#000000');
const [customColors, setCustomColors] = useState([]);
const [colorName, setColorName] = useState('');
const predefinedColors = [
{ name: 'Red', value: '#FF0000' },
{ name: 'Green', value: '#00FF00' },
{ name: 'Blue', value: '#0000FF' },
{ name: 'Yellow', value: '#FFFF00' },
{ name: 'Purple', value: '#800080' },
{ name: 'Orange', value: '#FFA500' }
];
const handleColorSelect = (color) => {
setSelectedColor(color);
};
const addCustomColor = (event) => {
[Link]();
if ([Link]()) {
setCustomColors([...customColors, {
name: colorName,
value: selectedColor
}]);
setColorName('');
}
};
const boxStyle = {
width: '200px',
height: '200px',
backgroundColor: selectedColor,
border: '2px solid #333',
margin: '20px auto'
};
return (
<div className="color-picker">
<h2>Color Picker</h2>
<div style={boxStyle}></div>
<p>Selected Color: {selectedColor}</p>
<div>
<h3>Choose a Color:</h3>
<input
type="color"
value={selectedColor}
onChange={(e) => handleColorSelect([Link])}
/>
</div>
<div>
<h3>Predefined Colors:</h3>
{[Link]((color) => (
<button
key={[Link]}
onClick={() => handleColorSelect([Link])}
style={{
backgroundColor: [Link],
color: 'white',
margin: '5px',
padding: '10px',
border: 'none',
cursor: 'pointer'
}}
>
{[Link]}
</button>
))}
</div>
<div>
<h3>Save Custom Color:</h3>
<form onSubmit={addCustomColor}>
<input
type="text"
value={colorName}
onChange={(e) => setColorName([Link])}
placeholder="Color name"
/>
<button type="submit">Save</button>
</form>
</div>
The colored box changes instantly as you select colors. Custom color buttons appear with their
saved background colors. The predefined color buttons are styled with their respective colors
(red button is red, blue button is blue, etc.).
2.8 Best Practices and Common Patterns
As you begin building React applications, following established best practices will help you create
maintainable and scalable code. One fundamental principle is to keep components small and
focused. Each component should have a single, clear responsibility. When a component starts
handling too many concerns, it's time to break it into smaller components.
Another important principle is to lift state up when multiple components need to share the same
changing data. Instead of duplicating state in multiple components, keep it in their closest
common ancestor and pass it down through props. This ensures a single source of truth for your
data and makes your application's data flow more predictable.
Components should also be pure whenever possible. A pure component always renders the same
output for the same props and state. This predictability makes components easier to test and
reason about. Avoid side effects in the render method, and instead use event handlers or React's
built-in hooks for side effects.
Consistent naming conventions make code more readable and maintainable. React components
should use PascalCase naming, starting with a capital letter. Event handler functions typically
start with "handle" followed by the event name, like handleClick or handleSubmit. State setter
functions follow the pattern "set" plus the state variable name.
Props that represent event handlers are typically prefixed with "on", such as onClick or onSubmit.
This convention clearly distinguishes props that pass data from those that pass callback functions.
Boolean props often start with "is", "has", or "should" to indicate their true/false nature.
File organization is also important. Each component should typically live in its own file, named
after the component. Related components can be grouped in directories. Style files, if used,
should follow a similar naming pattern and be co-located with their components.
While React is generally fast, understanding some performance basics helps build efficient
applications from the start. React re-renders components when their state or props change. This
is usually fine, but unnecessary re-renders can impact performance in complex applications.
One simple optimization is to avoid creating new objects or functions inside the render method
when possible. Each render creates new instances, which can cause child components to re-
render even when the data hasn't actually changed. For event handlers, define them as
component methods rather than inline arrow functions when performance is a concern.
Another consideration is list rendering. When rendering lists of components, always provide a
unique key prop. React uses keys to identify which items have changed, been added, or been
removed. Using array indices as keys can cause issues when the list order changes, so prefer
stable, unique identifiers when available.
2.9 Summary
This chapter introduced you to React, one of the most popular libraries for building user
interfaces. We began by understanding React's core philosophy, including its declarative nature,
component-based architecture, and unidirectional data flow. These principles form the
foundation of React development and distinguish it from traditional web development
approaches.
We explored the Virtual DOM, React's innovative solution to performance challenges in dynamic
web applications. By maintaining a lightweight representation of the DOM in memory and
efficiently updating only what has changed, React enables smooth, responsive user interfaces
even with frequent updates.
The practical portion of this chapter walked you through setting up a React development
environment using [Link] and Create React App. We examined the project structure and
understood the role of each key file and directory. This foundation prepares you for building
React applications throughout the rest of this course.
We then dove into components, the building blocks of React applications. You learned to create
functional components, organize them effectively, and understand how they work together to
create complete user interfaces. We explored props as a mechanism for passing data between
components, making them flexible and reusable.
State management through the useState hook brought interactivity to our components. We saw
how state allows components to respond to user actions and update their display accordingly.
Through practical examples like counters, forms, and todo lists, we demonstrated how state and
props work together to create dynamic applications.
The chapter concluded with best practices and patterns that will serve you well as you continue
your React journey. Following these conventions and principles will help you write clean,
maintainable React code that scales well as your applications grow.
In the next chapter, we'll explore the lifecycle of components in more detail, understanding how
React components are created, updated, and removed from the DOM. This deeper
understanding will enable you to build more sophisticated applications and handle complex
scenarios effectively.
Learning Objectives
A component lifecycle refers to the series of phases that a software component goes through
from its creation to its destruction. This concept is fundamental in modern software development
frameworks, particularly in front-end technologies like React, Vue, and Angular, where
components serve as the building blocks of user interfaces. The lifecycle encompasses distinct
stages including initialization, mounting, updating, and unmounting, each providing specific
opportunities for developers to execute code at precise moments during the component's
existence. Understanding these phases allows developers to manage resources efficiently,
handle data fetching, set up event listeners, and perform cleanup operations at the appropriate
times.
In the realm of [Link], the life cycle of a component encompasses a sequence of events and
methods that occur from the moment the component is loaded into memory until it persists in
memory. These events and methods, collectively known as the component's life cycle, play a vital
role in its creation, rendering, updating, and eventual destruction, However, with the
introduction of React Hooks, a more flexible and comprehensive approach to managing
component lifecycle and state has emerged. Hooks provide a paradigm shift, enabling developers
to utilize functions instead of class-based components, thus simplifying and enhancing the
management of component lifecycle and state. (N Poornima & O Kavita 2023).
Each lifecycle phase serves a specific purpose and offers hooks or methods that developers can
leverage to control component behavior. During the mounting phase, components are created
and inserted into the DOM, making it ideal for initial data loading and setup operations. The
updating phase occurs when component state or props change, triggering re-renders and
allowing for performance optimizations through comparison of previous and current values.
Finally, the unmounting phase provides a crucial opportunity to clean up resources, cancel
network requests, and remove event listeners to prevent memory leaks. This systematic
approach to component management ensures predictable behavior, optimal performance, and
maintainable code architecture in complex applications.
3.2 Lifecycle Phases in Class Components
The lifecycle of the component is divided into four phases listed below.
Source: Awesome [Link] (Unleash the Power of Modern UI Building), By N Poornima & O
Kavita 2023.
The useEffect hook serves as the primary mechanism for handling lifecycle events in functional
components, effectively replacing the multiple lifecycle methods found in class components with
a single, versatile API. This hook accepts a function that contains the side effect code and an
optional dependency array that controls when the effect should run. When used without a
dependency array, useEffect runs after every render, mimicking the behavior of
componentDidUpdate. With an empty dependency array, it runs only once after the initial render,
similar to componentDidMount. The hook also supports cleanup functions by returning a
function from the effect, which executes when the component unmounts or before the effect
runs again, providing the equivalent functionality of “componentWillUnmount”.
Beyond the basic useEffect patterns, functional components can leverage multiple hooks and
advanced techniques to create sophisticated lifecycle management strategies. Custom hooks can
be created to encapsulate complex lifecycle logic, making it reusable across different
components and promoting cleaner code organization.
Mounting Equivalent
Here's an example usage of getDerivedStateFromProps() component life cycle method:
[Link]
[Link]([Link])
[Link]([Link])
return {
value: [Link]
};
return null;
constructor(props) {
super(props);
[Link] = {
value: [Link]
render() {
return (
<div>
<h2>Value: {[Link]}</h2>
</div>
);
The Mounting equivalent demonstrates the Functional components replicating the mounting
phase of class components using the useEffect hook. By passing an empty dependency array ([]),
React ensures the effect runs only once after the initial render. This behavior mirrors
componentDidMount in class components.
The values of current prop and previous state are logged to the console as shown in the following figure:
Change the value of prop to ‘MBA’ as shown below: With this change, the following output is generated
in the browser:
Updating Equivalent
jsx
useEffect(() => {
[Link]("Component updated!");
}, [someStateOrProp]);
The code demonstrates the uses of the useEffect hook to mimic the update phase in React
functional components. It runs the effect only when someStateOrProp changes, as specified in
the dependency array. This behavior mirrors componentDidUpdate in class components and is
commonly used for side effects like logging or data fetching.
Unmounting Equivalent
jsx
useEffect(() => {
return () => {
};
}, []);
The code demonstrates the uses of useEffect with an empty dependency array to run once when
the component mounts. Inside the effect, a timer is started, and the returned function acts as a
cleanup that stops the timer. This cleanup function runs when the component unmounts,
simulating componentWillUnmount in class components.
jsx
componentDidMount() {
fetch("/api/user")
render() {
return <div>{[Link]?.name}</div>;
Functional Component:
jsx
function User() {
const [user, setUser] = useState(null);
useEffect(() => {
fetch("/api/user")
.then(setUser);
}, []);
return <div>{user?.name}</div>;
The lifecycle of a component describes the entire path of a software component from its
inception to its termination in contemporary frontend frameworks such as React, Vue, and
Angular. This includes three essential stages: mounting (initial creation and DOM integration for
one-time setup tasks like data fetching), updating (managing state and prop alterations that can
happen repeatedly for dynamic UI enhancements and performance improvements), and
unmounting (cleanup and resource management to avert memory leaks prior to component
removal).
1. Analyze the differences between class component lifecycle methods and functional
component hooks in React. In your analysis, discuss:
2. A developer notices that their React application is experiencing memory leaks. Explain how
improper lifecycle management could cause this issue and provide specific examples of what
should be cleaned up during the unmounting phase.
[Link] and contrast the three main lifecycle phases (Mounting, Updating, Unmounting) in
terms of:
4. Evaluate the statement: "The useEffect hook with an empty dependency array [] is exactly
equivalent to componentDidMount." Support your evaluation with technical reasoning.
Performance Optimization
5. Explain how lifecycle methods can be used to optimize component performance in both class
and functional components.
6. Design a strategy using dependency arrays in useEffect to prevent unnecessary API calls when
only specific props change. Include code snippets to illustrate your approach.
Problem-Solving Scenario
You are tasked with converting a legacy class component to a functional component. The class
component has the following lifecycle methods:
7. Outline the conversion strategy you would use, explaining how each class lifecycle method
would be replaced with hooks.
8. Identify potential challenges in this conversion and how you would address them.
Component Implementation
Requirements:
Debugging Challenge
2. The following functional component has several lifecycle-related issues. Identify and fix all
problems:
jsx
useEffect(() => {
fetchUser(userId).then(setUser);
fetchUserPosts(userId).then(setPosts);
});
useEffect(() => {
});
}, []);
return (
<div>
<h1>{user?.name}</h1>
</div>
);
Your task:
Requirements:
• Implement proper lifecycle management
Learning Objectives
• Identify key differences between JSX and HTML attributes (className vs class, htmlFor vs for).
• Apply inline styles using the style attribute with object syntax.
JSX (JavaScript XML) is an extension of syntax for JavaScript that enables you to write code similar
to HTML directly in your JavaScript files. Created by Facebook for React, JSX connects the logic of
your application with its visual representation, offering a more intuitive and effective approach
to constructing user interfaces.
At first sight, JSX may seem like a combination of HTML and JavaScript, and that's precisely the
intention. It merges the well-known framework of markup with the dynamic features of
JavaScript, enabling developers to build interactive and adaptable user interfaces with
unmatched simplicity and versatility.
Within every React component, there is an embedded render() function that plays a crucial role
in generating the HTML output rendered in a browser. This function defines how the
component's UI should be displayed and is responsible for returning the JSX (JavaScript XML)
code that represents the component's structure and content. However, JSX itself is not
understood by JavaScript engines. It needs to be transformed into standard JavaScript code
before it can be executed. This transformation process is typically handled by preprocessors like
Babel, which convert JSX expressions into regular JavaScript objects that can be understood and
parsed by the JavaScript engine.
By using JSX, developers can write code that closely resembles the final HTML structure and
achieve a more concise and readable representation of the component's UI. This allows for easier
maintenance, better collaboration between developers, and improved code organization. The
render() function in a React component is responsible for generating the HTML output that is
rendered in a browser. JSX, an extension of React, enables developers to write JavaScript
functions using a syntax similar to HTML. Preprocessors like Babel transform JSX expressions into
standard JavaScript objects that can be executed by JavaScript engines, allowing for more
intuitive and expressive component definitions.
<div>Hello World</div>
Enhanced Performance: React's code optimization techniques during the translation process
make it faster than regular JavaScript. It employs a virtual DOM that efficiently updates only the
modified components instead of re-rendering the entire HTML structure, resulting in improved
performance and a smoother user experience.
Consolidated Markup and Logic: Unlike traditional web applications where markup and logic
reside in separate HTML and JavaScript files, React combines them within components. This
consolidation simplifies code organization and promotes a more cohesive development approach,
allowing developers to easily manage and maintain both the visual representation and the
functionality of the application.
Type Safety: React offers a type-safe development environment, where most errors are caught
during compilation rather than at runtime. By utilizing tools like TypeScript or PropTypes,
developers can define and enforce strict type checks, enabling early detection of potential errors
and improving overall code reliability.
Streamlined Template Creation: React simplifies the creation of templates by utilizing JSX, a
syntax extension that allows developers to write HTML-like code directly within JavaScript. This
approach provides a more intuitive and concise way to define component structures and their
dynamic behaviour. JSX is then transformed into standard JavaScript objects, facilitating template
creation and maintenance.
JSX follows specific syntax rules that differentiate it from regular HTML while maintaining familiar
markup patterns. Understanding these rules is essential for effective React development.
function MyComponent() {
return (
<div>
<h1>Title</h1>
<p>Content</p>
</div>
);
function MyComponent() {
return (
<>
<h1>Title</h1>
<p>Content</p>
</>
);
function MyComponent() {
return (
<h1>Title</h1>
);
}
Self-Closing Tags: All tags must be properly closed, including self-closing tags that require a
forward slash.
// Correct
<br />
// Incorrect
<input type="text">
<br>
JavaScript Expressions: Use curly braces {} to embed JavaScript expressions within JSX.
function Greeting() {
return (
<div>
<h1>Hello, {name}!</h1>
);
Key Differences:
<div class="container">
<label for="email">Email:</label>
</div>
// JSX
<div className="container">
<label htmlFor="email">Email:</label>
</div>
4.3 Conditional Rendering in JSX
Conditional rendering allows you to display different content based on certain conditions. JSX
provides several methods to implement conditional rendering.
return (
<div>
{isLoggedIn ? (
):(
)}
</div>
);
// Usage
return (
<div>
<h1>Dashboard</h1>
{hasMessages && (
<div className="notification">
</div>
)}
</div>
);
switch(status) {
case 'loading':
case 'success':
case 'error':
default:
return <div>Ready</div>;
};
return (
<div className="status-container">
{getStatusMessage()}
</div>
);
JSX provides a synthetic event system that wraps native DOM events, providing consistent
behavior across different browsers.
4.4.1 Basic Event Handling
Event handlers in JSX are passed as functions and use camelCase naming convention.
function ButtonExample() {
[Link]('Button clicked!');
};
};
return (
<button
onClick={handleClick}
onMouseOver={handleMouseOver}
>
Click Me
</button>
);
}
4.4.2 Form Event Handling
Handling form events requires special attention to controlled components and state
management.
function ContactForm() {
name: '',
email: '',
message: ''
});
setFormData(prevData => ({
...prevData,
[name]: value
}));
};
[Link]();
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
name="name"
value={[Link]}
onChange={handleInputChange}
placeholder="Your Name"
/>
<input
type="email"
name="email"
value={[Link]}
onChange={handleInputChange}
placeholder="Your Email"
/>
<textarea
name="message"
value={[Link]}
onChange={handleInputChange}
placeholder="Your Message"
/>
<button type="submit">Send Message</button>
</form>
);
return (
<ul>
{[Link](item => (
<li
key={[Link]}
>
{[Link]}
</li>
))}
</ul>
);
}
4.5 Styling in JSX
JSX provides multiple approaches to styling components, with inline styles being one of the
primary methods recommended by React.
const divStyle = {
backgroundColor: 'blue',
color: 'white',
fontSize: '20px',
padding: '10px',
borderRadius: '5px',
marginBottom: '15px'
};
const dynamicStyle = {
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
};
return (
<div>
<div style={divStyle}>
</div>
<div style={dynamicStyle}>
Dynamic styling
</div>
</div>
);
};
const containerStyle = {
width: '100%',
height: '20px',
backgroundColor: '#f0f0f0',
borderRadius: '10px',
overflow: 'hidden'
};
const progressStyle = {
width: `${percentage}%`,
height: '100%',
backgroundColor: color,
};
return (
<div style={containerStyle}>
<div style={progressStyle}></div>
</div>
);
import './[Link]';
function Button({ variant, size, children }) {
return (
<button className={className}>
{children}
</button>
);
transform: 'scale(1.02)'
} : {};
return (
<h3 className="card-title">{title}</h3>
<p className="card-content">{content}</p>
</div>
);
}
return (
<div className="product-list">
{[Link](product => (
<h3>{[Link]}</h3>
<p>${[Link]}</p>
</div>
))}
</div>
);
}
return (
<ul>
{[Link](todo => (
<li key={[Link]}>
<span>{[Link]}</span>
<input
type="checkbox"
checked={[Link]}
/>
</li>
))}
</ul>
);
return (
<ul>
<span>{[Link]}</span>
</li>
))}
</ul>
);
return (
<div className="product-card">
<h3>{[Link]}</h3>
Add to Cart
</button>
</div>
);
onAddToCart([Link]);
}, [[Link], onAddToCart]);
return (
<div className="product-card">
<h3>{[Link]}</h3>
<button onClick={handleAddToCart}>
Add to Cart
</button>
</div>
);
JSX supports all HTML accessibility attributes, making it easy to build inclusive web applications.
4.7.1 Semantic HTML Elements
Use appropriate semantic HTML elements to provide meaning and structure.
return (
<article>
<header>
<h1>{title}</h1>
<p>
By <span className="author">{author}</span>
<time dateTime={publishDate}>
{new Date(publishDate).toLocaleDateString()}
</time>
</p>
</header>
<main>
<p>{content}</p>
</main>
</article>
);
return (
<>
<button
aria-expanded={isOpen}
aria-controls="navigation-menu"
onClick={toggleMenu}
>
Menu
</button>
<nav
id="navigation-menu"
aria-hidden={!isOpen}
role="navigation"
>
<ul role="menubar">
<li role="none">
</li>
<li role="none">
</li>
</ul>
</nav>
</>
);
function AccessibleForm() {
return (
<form>
<div className="form-group">
<label htmlFor="email">
Email Address
<span aria-label="required">*</span>
</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail([Link])}
aria-describedby="email-help email-error"
aria-invalid={!![Link]}
required
/>
</div>
{[Link] && (
{[Link]}
</div>
)}
</div>
</form>
);
}
4.8 Best Practices and Common Patterns
return (
<div className="card">
<div className="card-body">{children}</div>
</div>
);
return (
<Card
title={<h2>{[Link]}</h2>}
footer={<button>Edit Profile</button>}
>
<p>{[Link]}</p>
</Card>
);
// Instead of this
function UserCard(props) {
return (
<div>
<h3>{[Link]}</h3>
<p>{[Link]}</p>
<button onClick={[Link]}>Edit</button>
</div>
);
//Use this
return (
<div>
<h3>{name}</h3>
<p>{email}</p>
<button onClick={onEdit}>Edit</button>
</div>
);
JSX is a powerful syntax extension that bridges the gap between JavaScript logic and HTML
markup in React applications. It provides an intuitive way to describe user interfaces while
maintaining the full power of JavaScript. Key takeaways from this chapter include:
JSX transforms HTML-like syntax into JavaScript function calls, making component development
more intuitive and maintainable. The syntax differs from HTML in specific ways, such as using
className instead of class and htmlFor instead of for, following JavaScript naming conventions.
Conditional rendering in JSX can be achieved through ternary operators, logical AND operators,
and function-based approaches, providing flexibility in displaying dynamic content based on
application state.
Event handling in JSX uses a synthetic event system that provides consistent cross-browser
behavior, with event handlers passed as functions using camelCase naming conventions.
Styling in JSX can be accomplished through inline styles using JavaScript objects with camelCase
property names, allowing for dynamic styling based on component state and props.
Performance considerations include proper use of keys in lists, avoiding inline function creation
in render methods, and understanding how the virtual DOM optimizes updates.
Accessibility in JSX is achieved by using semantic HTML elements, proper ARIA attributes, and
ensuring forms are properly labeled and described for screen readers and other assistive
technologies.
By mastering these JSX concepts and patterns, developers can build robust, performant, and
accessible React applications that provide excellent user experiences across different devices and
browsers.
5 Building Apps in ReactJS
Learning Objectives
ReactJS represents a paradigm shift in how we approach building user interfaces for web
applications. Developed by Facebook (now Meta) in 2013, React has fundamentally changed the
landscape of front-end development by introducing a component-based architecture that
promotes reusability, maintainability, and scalability.
The emergence of React addressed several critical challenges that developers faced with
traditional DOM manipulation approaches. Before React, developers often struggled with
complex state management, inefficient DOM updates, and maintaining large-scale applications.
React's declarative approach and innovative concepts like the Virtual DOM have made it one of
the most popular JavaScript libraries for building user interfaces.
Understanding React is crucial for modern web developers, as it forms the foundation for
numerous frameworks and influences how we think about user interface development. This
chapter will provide you with a comprehensive understanding of React's core concepts, its
applications, and why it has become an industry standard.
• Code Editor: Visual Studio Code is the most popular choice, enhanced with extensions like
ESLint, Prettier, and React snippets for better development experience
Development Tools:
• React Developer Tools: Browser extension for Chrome/Firefox that allows inspection of React
component hierarchies and debugging
• Project Scaffolding: Either Create React App (npx create-react-app my-app) or Vite (npm create
vite@latest my-app) to quickly set up new React projects with proper configuration
To start using React, you can create a new project with Create React App:
Recommended IDE is Visual Studio Code & NodeJS please download & install on the following links
below
1. [Link]
2. [Link]
Step One. Create a folder on the desktop and name it ReactJS (any othername will do as well)
Step Three. Open Terminal tab and select New Termianal enter the following commands
1. npm create vite@latest
2. give your project a name
3. select a framework (React)
4. select varient (JavaScript)
5. Now runthe following commands
• cd new-app (app name if you have given a different name, please use that name )
• npm install
• npm run dev
Step 6. Copy localhost and paste in Chrome browser (recomemended)
[Link]
React applications are constructed from components - individual UI elements that combine their
own functionality with visual presentation. Components can range from simple elements like
buttons to complex structures like complete pages.
React components are JavaScript functions that output markup code:
The core concept remains the same: components serve as the fundamental building blocks of
React applications, each encapsulating both behavior and display logic within reusable JavaScript
functions that generate the necessary markup for rendering.
[Link]
Browser Output
A key distinction in React! The capitalization rule helps React differentiate between:
This naming convention is crucial because React uses it to determine whether to render a custom
component or a built-in HTML element.
The MyButton component gets rendered wherever <MyButton /> appears in the JSX, making it a
reusable piece of UI that can be easily included in different parts of your application.
[Link] [Link]
5.7 What is JSX in React?
JSX (JavaScript XML) is a syntax extension for JavaScript that allows you to write HTML-like code
directly within JavaScript files. It was created by Facebook specifically for React to make
component creation more intuitive and readable.
React's Virtual DOM acts like an architect's draft blueprint - a fast, lightweight copy kept in
memory. When changes are needed, React first updates this digital sketch, compares it with the
previous version, then efficiently applies only the necessary modifications to the actual Browser
DOM. This approach is like planning renovations on paper before touching the real building,
resulting in much faster and smoother updates.
1. State Changes: React detects when data in your application has been modified and identifies
which components need attention
2. Calculate Updates: React intelligently determines exactly what has changed by comparing the
current Virtual DOM with the previous version, identifying only the specific differences
3. Efficient Re-rendering: The actual Browser DOM receives only the precise updates needed - no
unnecessary modifications. Through React's optimization strategies, your application maintains
peak performance and responsiveness, regardless of its complexity.
Components allow you to break down your user interface into separate, reusable parts that you
can work on independently. Think of components as special JavaScript functions - they take in
data (called "props") and return instructions for what should be displayed on the screen.
This modular approach makes your code easier to manage, test, and reuse across different parts
of your application.
Key Concepts:
For developers creating dynamic and scalable web applications, ReactJS functional components
are an essential tool. ReactJS stands as one of the most widely-used JavaScript libraries,
empowering developers to build reusable UI elements for web applications. The introduction of
React hooks has transformed functional components into powerful building blocks, allowing
developers to implement sophisticated functionality with concise, readable code. This guide
examines ReactJS functional components, their mechanics, and their role in creating
maintainable applications.
ReactJS functional components are JavaScript functions that output JSX elements - templates
defining the component's visual structure. JSX combines HTML-like syntax with JavaScript
capabilities, creating a seamless development experience. While originally designed for
presentational purposes without state or complex behavior, modern functional components can
handle any use case through hooks. Their straightforward nature makes them lightweight,
testable, and perfect for building user interfaces.
1. Clarity: Their function-based structure creates more readable and maintainable code,
particularly beneficial for projects of any size.
2. Efficiency: Without class instantiation overhead or this binding complexities, functional
components deliver superior rendering performance.
3. Testing Ease: Pure function nature makes them straightforward to test, with predictable inputs
and outputs simplifying unit testing strategies.
4. Modularity: High reusability across different projects makes them valuable for component
libraries and design systems.
Here's a basic example of a React Functional Component called App that returns JSX:
1. Default Choice: Functional components should be your go-to option for new React
development. They work for both simple presentational components and complex stateful
components.
2. State Management: With React Hooks (introduced in React 16.8), functional components can
handle state using useState, useReducer, and other hooks, eliminating the previous limitation.
3. Lifecycle Operations: Hooks like useEffect provide all the functionality previously available only
in class component lifecycle methods.
Modern Context
The information about functional components being limited is outdated. Since React 16.8,
functional components with hooks can:
1. Simplicity: Cleaner syntax with less boilerplate code compared to class components.
2. Performance: Generally, more efficient due to reduced overhead and better optimization by
React.
3. Testing: Easier to test since they're pure functions that take props and return JSX.
4. Modern Patterns: Better integration with modern React patterns like hooks, concurrent
features, and the latest React development tools.
5. Code Reusability: Custom hooks allow sharing stateful logic between components more
effectively than class-based patterns.
Props function as parameters for React functional components, making components flexible and
reusable. The parent component (App) determines what data to pass down, while the child
component (Headline) remains generic and adaptable. This pattern allows the same component
to render different content based on the props it receives.
When you render a component, props are passed using HTML-like attribute syntax. The receiving
component accesses these props through the function's first parameter, which is always a props
object containing all passed data.
Props Destructuring
Since props arrive as an object and you typically need specific values, JavaScript destructuring
provides a cleaner approach:
Further reading
Event handling is a fundamental concept in React that enables user interaction with your
application. Let's explore how to implement event handlers in functional components, starting
with the most common scenarios.
The simplest event handler example involves a button with an onClick attribute. This attribute
accepts a function that executes whenever the button is clicked:
Arrow Function Syntax
Event handlers can be written using arrow functions for a more concise approach:
However, when multiple event handlers exist in a component, using function declarations can
improve code organization and readability
Event handlers typically implement business logic, often involving state updates. Here's an
example using React's useState hook:
Input fields require access to the event object, which React passes as the first parameter to event
handlers. This synthetic event wraps the native HTML event and provides additional functionality:
Controlled Components
To create a controlled component, pass the state value back to the input element through the
value attribute:
Updating the screen
Often, you’ll want your component to “remember” some information and display it. For example,
maybe you want to count the number of times a button is clicked. To do this, add state to your
component.
Functions that begin with use are called Hooks. The useState function is a built-in Hook that React
provides. You can discover additional built-in Hooks in the API documentation. It's also possible
to create custom Hooks by combining existing ones.
Hooks have stricter rules than regular functions. You must call Hooks only at the top level of your
components (or within other Hooks). If you need to use useState inside a conditional statement
or loop, you should create a separate component and place it there instead.
In the earlier example, each MyButton maintained its own separate count value, and clicking any
individual button only affected that specific button's count:
This demonstrates component isolation - each instance of MyButton has its own state that
operates independently. When you interact with one button, it doesn't influence the state of
other button instances on the page. Each component maintains its own private state unless
explicitly designed to share data with other components.
The
However, often you’ll need components to share data and always update together.
To make both MyButton components display the same count and update together, you need to
move the state from the individual buttons “upwards” to the closest component containing all of
them.
Now when you click either button, the count in MyApp will change, which will change both of the
counts in MyButton. Here’s how you can express this in code.
Finally, change MyButton to read the props you have passed from its parent component:
When you click the button, the onClick handler fires. Each button’s onClick prop was set to the
handleClick function inside MyApp, so the code inside of it runs. That code calls setCount(count
+ 1), incrementing the count state variable. The new count value is passed as a prop to each
button, so they all show the new value. This is called “lifting state up”. By moving state up, you’ve
shared it between components.
5.7 Working with Hooks
In React, functions that begin with use are known as Hooks. The useState Hook is one of React's
built-in offerings, and you can explore additional built-in Hooks in the official API documentation.
You also have the flexibility to create custom Hooks by combining existing ones.
Hooks come with specific usage rules that make them more restrictive than regular functions.
They must be called at the top level of your components or other Hooks. If you need to use
useState within a conditional statement or loop, the solution is to extract that logic into a separate
component.
In the earlier example, each MyButton component maintained its own separate count state.
When a button was clicked, only that specific button's count value would update, leaving the
other buttons unaffected
In many cases, you'll want components to share the same data and update synchronously.
To have both MyButton components show identical count values and change together, you must
relocate the state from each individual button "up" to their nearest shared parent component.
When you click the button, the onClick handler is triggered. Since each button's onClick prop
points to the handleClick function defined in MyApp, that function executes. The function calls
setCount(count + 1), which increases the count state variable by one. This updated count value
gets passed down as a prop to each button, ensuring they all display the same new value. This
pattern is known as "lifting state up" - by relocating state to a higher level, you enable multiple
components to share it.
Example
5.7.1 [Link]
Browser Output
5.8 Lists and Keys in React JS
React employs lists to dynamically render multiple components, with keys serving as identifiers
that help React efficiently detect which items have been modified, inserted, or deleted.
Keys provide React with a way to uniquely identify elements during list rendering. This enhances
performance by reducing unnecessary re-renders, as React relies on keys to monitor changes
within dynamic lists.
Lists represent collections of elements that you can render using JSX syntax. JSX enables you to
write HTML-like code within JavaScript and use it to build React components. Here's an example
of creating a number list using an array and JSX:
The map() method is a JavaScript array function that accepts a callback function and executes it
on every array element, producing a new array containing the results. In this scenario, we're using
an arrow function that receives a number parameter and returns an <li> element containing that
number as its content.
Here, we use .map() to iterate over the fruits array and render each item inside an <li>.
Browser Output
Here, each object has a unique id, which we use as the key for better performance.
Browser Output
Example 3: Adding Items to a List Dynamically
The list updates dynamically when clicking the "Add Item" button.
Browser Output
Example 4: Removing an Item from a List
Forms are essential components in web applications that allow users to input and submit data.
React provides several approaches to handle forms, with controlled components being the most
common and recommended pattern.
Controlled Components
In controlled components, form data is handled by React state rather than the DOM. The input's
value is controlled by React state, and changes are handled through event handlers.
To create interactive controls for submitting information, render the built-in browser <form>
component:
Managing Form Submission on the Client Side
You can pass a function to the action prop of a form to execute that function when the form is
submitted. The function will receive formData as a parameter, allowing you to access all the data
submitted through the form. This approach differs from traditional HTML forms, where the action
attribute only accepts URLs. Once the action function completes successfully, all uncontrolled
form field elements are automatically reset to their initial state.
Browser Output
In this approach, the pending property serves as an indicator that the form is currently processing
the submission.
[Link]
Sometimes the function executed by a <form>'s action prop will throw an error. You can manage
these errors by enclosing the <form> within an Error Boundary component. When the function
triggered by the <form>'s action prop encounters an error, the Error Boundary's fallback
component will be rendered instead of the form.
[Link]
[Link]
import React, { useState } from "react";
import "./[Link]";
function App() {
const [formData, setFormData] = useState({
fullName: "",
email: "",
phone: "",
password: "",
});
return (
<div className="signup-container">
<h2>Gym Membership Sign Up</h2>
<form onSubmit={handleSubmit} className="signup-form">
<label>
Full Name:
<input
type="text"
name="fullName"
value={[Link]}
onChange={handleChange}
required
/>
</label>
<label>
Email Address:
<input
type="email"
name="email"
value={[Link]}
onChange={handleChange}
required
/>
</label>
<label>
Phone Number:
<input
type="tel"
name="phone"
value={[Link]}
onChange={handleChange}
required
/>
</label>
<label>
Password:
<input
type="password"
name="password"
value={[Link]}
onChange={handleChange}
required
/>
</label>
.signup-form {
display: flex;
flex-direction: column;
}
.signup-form label {
margin-bottom: 15px;
font-weight: bold;
}
.signup-form input {
padding: 8px;
margin-top: 5px;
border: 1px solid #ccc;
border-radius: 5px;
}
.signup-form button {
padding: 10px;
background-color: #a72887;
color: rgb(255, 255, 255);
border: none;
border-radius: 5px;
font-size: 16px;
cursor: pointer;
}
.signup-form button:hover {
background-color: #218838;
}
Browser Output
Doctor Appointment Booking Example
[Link]
import React, { useState } from "react";
import "./[Link]";
function App() {
const [formData, setFormData] = useState({
fullName: "",
email: "",
phone: "",
doctor: "",
date: "",
reason: "",
});
// Reset form
setFormData({
fullName: "",
email: "",
phone: "",
doctor: "",
date: "",
reason: "",
});
};
return (
<div className="form-container">
<h2>Doctor Appointment Booking</h2>
<form onSubmit={handleSubmit} className="appointment-form">
<label>
Full Name:
<input
type="text"
name="fullName"
value={[Link]}
onChange={handleChange}
required
/>
</label>
<label>
Email Address:
<input
type="email"
name="email"
value={[Link]}
onChange={handleChange}
required
/>
</label>
<label>
Phone Number:
<input
type="tel"
name="phone"
value={[Link]}
onChange={handleChange}
required
/>
</label>
<label>
Select Doctor:
<select
name="doctor"
value={[Link]}
onChange={handleChange}
required
>
<option value="">-- Choose Doctor --</option>
<option value="Smith">Dr. Smith</option>
<option value="Patel">Dr. Patel</option>
<option value="Moyo">Dr. Moyo</option>
</select>
</label>
<label>
Appointment Date:
<input
type="date"
name="date"
value={[Link]}
onChange={handleChange}
required
/>
</label>
<label>
Reason for Visit:
<textarea
name="reason"
value={[Link]}
onChange={handleChange}
required
></textarea>
</label>
[Link]
.form-container {
max-width: 500px;
margin: 50px auto;
padding: 25px;
background-color: #f7f9fa;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.appointment-form {
display: flex;
flex-direction: column;
}
.appointment-form label {
margin-bottom: 15px;
font-weight: 500;
}
.appointment-form input,
.appointment-form select,
.appointment-form textarea {
padding: 10px;
margin-top: 5px;
border-radius: 5px;
border: 1px solid #ccc;
width: 100%;
}
.appointment-form textarea {
resize: vertical;
height: 80px;
}
.appointment-form button {
padding: 12px;
background-color: #007bff;
color: white;
font-size: 16px;
border: none;
border-radius: 6px;
cursor: pointer;
margin-top: 10px;
}
.appointment-form button:hover {
background-color: #0056b3;
}
Browser Output
[Link]
import React, { useState } from "react";
import "./[Link]"; // Create this file
function DarkModeToggle() {
const [dark, setDark] = useState(false);
return (
<div className={dark ? "dark-mode" : "light-mode"}>
<h1>{dark ? "Dark" : "Light"} Mode</h1>
<button
className={dark ? "btn-dark" : "btn-light"}
onClick={() => setDark(!dark)}
>
Toggle {dark ? "Light" : "Dark"} Mode
</button>
</div>
);
}
[Link]
.dark-mode,
.light-mode {
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-family: Arial, sans-serif;
text-align: center;
transition: background-color 0.5s, color 0.5s;
}
.dark-mode {
background-color: #333;
color: #fff;
}
.light-mode {
background-color: #fff;
color: #000;
}
.btn-dark,
.btn-light {
padding: 12px 24px;
font-size: 16px;
border: none;
border-radius: 6px;
cursor: pointer;
margin-top: 20px;
transition: background-color 0.3s ease;
}
.btn-dark {
background-color: #555;
color: #fff;
}
.btn-dark:hover {
background-color: #666;
}
.btn-light {
background-color: #007bff;
color: #fff;
}
.btn-light:hover {
background-color: #0056b3;
}
Browser Output
6 References
1. Naik, P.G. and Oza, K.S. (2023) Awesome [Link]: unleash the power of modern UI building.
International Institute of Organized R
2. Banks, A. and Porcello, E. (2017) Learning React: functional web development with React and
Redux. Sebastopol, CA: O'Reilly Media.