Module 3 - Node JS Rest API
Module 3 - Node JS Rest API
A massive industry relevant skill enhancement initiative for the Youth of Tamil Nadu.
WEEK 1 – PROGRAM ORIENTATION
● React state management is an important concept while building applications in React. This is not necessarily required in all
applications but very crucial when it comes to managing state in complex and big applications.
● Being such a crucial concept in React, it is one of the most popular and widely used libraries in JavaScript world for building
dynamic web interfaces.
● React apps are built using components each of which manage their state internally. This works well when it comes to
applications with few components but as the application grows bigger, it becomes challenging to manage states which are
used across components and when there are concurrent updates to the same state. This is why we need to manage state in
React applications.
● When the state of a UI component changes, react automatically re-renders the component to reflect those changes on UI.
Managing state with useState
● useState is a built-in React Hook that allows functional components to maintain and update local
state. Before React Hooks, state management was limited to class components using this.state and
this.setState.
● With useState, stateful logic is now possible in functional components without using this.
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
// State to store count value
const [count, setCount] = useState(0);
// Function to increment count by 1
const incrementCount = () => {
// Update state with incremented value
setCount(count + 1);
};
return (
<div className="app">
<button onClick={incrementCount}>Click Here</button>
{count}
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
State management with Redux
What is Redux?
Redux is a predictable state container for JavaScript applications. It helps manage global
state in a centralized manner using a single store, making data flow predictable and
debuggable.
Redux is a JavaScript library to manage states in an application. It uses a single store to
hold the application’s state and a set of rules for updating the state which are called
reducers.
1. Actions
Action is an object which holds data payload, sent from application to store. This
is done using store.dispatch().
2. Reducers
Reducer is a function that take 2 inputs – the previous state and an action. It
returns the updated state based on the dispatched actions.
const initialState = {
counter: 0
};
const counterReducer = (state = initialState,
action) => {
switch (action.type) {
case "INCREMENT":
return { ...state, counter: state.counter + 1 };
case "DECREMENT":
return { ...state, counter: state.counter - 1 };
default:
return state;
}
};
export default counterReducer;
3. Store
Store is a container or a storage unit in the application. When any
component needs access to the state or needs to be updated, it interacts
with the store which then manages the data and propagates changes to
the relevant parts of the application. In short, it holds the application
state.
Exploring various Advanced React Hooks
1. State Hooks
➢ These hooks let components remember the state of information (for e.g. a user
input)
➢ We can use the below hooks to add state to a component.
a. useState – declares a state variable that can be updated directly.
const [state, setState] = useState(initialState)
b. useReducer – declares a state variable with logic inside a reducer function.
const [state, dispatch] = useReducer(reducer, initialArg, init?)
2. Context hooks
Context hooks in React provides a way to pass data through a component tree without the need to prop-drill
(i.e., pass props down manually at every level).
useContext reads and subscribes to a context and provides function components access to the context value
for a context object.
3. Ref hooks
These hooks let component hold information that’s not used for re-rendering like DOM node or timeout ID. Updating a
ref does not re-render the component. For this, we use a hook called useRef.
This returns a ref object with a single current property set to an initial value. In the next rendering, it returns the same
object. So these are used to store data that does not affect the visual output of our components.
4. Effect Hooks
These hooks are used to perform side effects in your functional components, such as fetching data, subscribing to
external events, or manually changing the DOM. It can be used also to interact with external systems. It combines the
functionality of componentDidMount, componentDidUpdate, and componentWillUnmount in class components.
useEffect(setup, dependencies?)
5. Performance Hooks
These hooks are used to overcome the performance drawbacks of re-rendering of components. Examples are:
a. useMemo - used to cache the result of a calculation between re-renders.
const cachedValue = useMemo(calculateValue, dependencies)
b. useCallback – used to cache a function definition between re-renders.
const cachedFn = useCallback(fn, dependencies)
Effect hook for side effects
Side-effects in React are operations that occur in the component after rendering. These include
subscriptions or fetching data from API. useEffect is used in such cases.
// 1. import useEffect
import { useEffect } from 'react';
function MyComponent() {
// 2. call it above the returned JSX
// 3. pass two arguments to it: a function and an array
useEffect(() => {}, []);
// return ...
}
The function passed to useEffect is a callback function. This will be called after the component
renders. The second argument is the dependencies array which include all of the values that our side
effect relies upon.
useEffect by default runs both after the first render and after every update.
In the example below, we have used useEffect to print count in every button click.
import React, { useState, useEffect } from "react";
import "./styles.css";
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("UseEffect example");
});
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>Click</button>
</div>
);
}
Context API for state management
● The Context API in React is a feature that allows you to manage the global state of your application without the need
to pass data through multiple levels of components using props.
● It provides a way to share data and functionality across different components, regardless of where they are in the
component tree. There are 2 parts to the Context API: Context Provider and Context Consumer.
● This function returns a Context object containing a Provider component and a Consumer
component.
● The Provider component wraps a section of component tree where we need to make it available.
The Consumer component is used to access the shared data within your components.
● Props drilling works fine in case of an application with small components, but as the application
grows, it becomes cumbersome to pass the state explicitly through props.
1.Create the Context
File: CountContext.js
import React, { createContext, useContext, useState } from "react";
return (
<CountContext.Provider value={{ count, setCount }}>
{children}
</CountContext.Provider>
);
};
return (
<div>
<p>Count: {count}</p>
</div>
);
➔ In the above example, we use the createContext function to create a
}; context for the count state. The CountProvider component wraps its
children with the context provider, making the count state accessible to
export default ChildComponent; all descendant components. ChildComponent consumes the count state
using the useCount custom hook provided by the CountContext.
Custom Hooks in React
● A custom hook is a special JavaScript function whose name starts with 'use' and can be
used to call other hooks.
● Custom hooks can be created by identifying repetitive logic across our application
components and then extracting this logic into a function named with the use prefix.
● Custom hooks should start with use, like useFormInput or useFetch, and can use React's
built-in Hooks within. It should return anything that will be useful for the component
using this hook. Each custom hook should be responsible for a single piece of
functionality.
Reusing Logic with Custom Hooks
● When an application component logic needs to be used by multiple components, we can use a custom Hook.
● A typical use case where we could use custom hooks is data fetching functionality. We can create a useFetch
hook if we need to fetch data from an API in multiple components.
function App() {
const { data: quote, loading, error } = useFetch('https://api.quotable.io/random');
return (
<div className="App">
{loading && <p>{loading}</p>}
{quote && <p>"{quote}"</p>}
{error && <p>{error}</p>}
</div>
);
}
Handling forms in React can be verbose. A custom hook, useFormInput, can simplify this. Here’s how
we might structure it:
useFormInput.js
import { useState } from 'react';
function useFormInput(initialValue) {
const [value, setValue] = useState(initialValue);
function FormComponent() {
const name = useFormInput('');
const age = useFormInput('');
const handleSubmit = (e) => {
e.preventDefault();
console.log(`Name: ${name.value}, Age: ${age.value}`);
};
return (
<form onSubmit={handleSubmit}>
<div>
<label>
Name:
{/* The spread operator is equivalent to value={name.value} onChange={name.onChange} */}
<input type="text" {...name} />
</label>
</div>
<div>
<label>
Age:
<input type="number" {...age} />
</label>
</div>
<button type="submit">Submit</button> ❖ The implementation of useFormInput provided here is a basic version,
</form> designed for straightforward form input management. If additional
); functionalities are required, such as form validation or handling more
}
complex form patterns, the Hook should be further customized and extended.
export default FormComponent;
Benefits of Using Custom Hooks
1. Code Reusability:
With custom hooks, you can encapsulate the logic for fetching data (e.g., making API calls, handling
loading and error states) into a single function. This function can then be reused across different
components, eliminating code redundancy.
3. Separation of Concerns:
Custom hooks help maintain a clear separation of concerns in your codebase. By extracting data fetching
logic into separate functions, you can ensure that your components remain focused on presentation
concerns, promoting better code organization and readability.
4. Testing:
Custom hooks can be easily tested in isolation, which enhances the overall testability of your React
components. By writing unit tests for your custom hooks, you can ensure that the data fetching logic
behaves as expected across different scenarios, without the need to test it within every component that uses
it.
React Routing
To configure React Router, navigate to the index.js file, which is the root file, and import
BrowserRouter from the react-router-dom package that we installed, wrapping it around
our App component.
index.js
import React from 'react';
import App from './App';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
All routes are wrapped in the Routes tag. Each Route has two properties:
App.js
import React from 'react';
import './App.css';
import { Routes, Route } from 'react-router-dom';
import Products from './Pages/Products';
import About from './Pages/About';
import Home from './Pages/HomePage';
function App() {
return (
<>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/products" element={<Products />} />
<Route path="/about" element={<About />} />
</Routes>
</>
);
}
This can be done by using the react-router library. React Router allows us to link to a new
URL and conditionally render components based on that URL.
Example:
● index.js
function App() {
return (
<>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/products" element={<Products />} />
<Route path="/about" element={<About />} />
</Routes>
</>
);
}
React Router's heart is the Router component. It's in charge of controlling the URL and ensuring that it
may match routes and details. Within the Router in React JS component, we declare routes as children.
At the core of every React Router application, we should have a router component. For web projects,
react-router-dom provides <BrowserRouter> and <HashRouter> routers.
Import:
● <Route> is used to match the current URL path with a component and render it.
Import:
Example:
Import:
import { Link } from 'react-router-dom';
import { NavLink } from 'react-router-dom';
● <NavLink> is used for navigating to different components within a single-page application. It renders as an
<a> tag in the DOM and is useful for navbars.
● <Redirect> allows dynamically redirecting a user from one route to another. It can override the history
object.
● Example – Using <Redirect>:
const withAuth = (Component) => {
const AuthRoute = () => {
const isAuth = !!localStorage.getItem("token");
if (isAuth) {
return <Component />;
} else {
return <Redirect to="/" />;
}
};
return AuthRoute;
};
Advanced React Patterns
Advanced React Patterns make significant enhancements to the structure and
maintainability of applications. Some of the common and useful patterns are:
1. Render Props
Render Props is a React pattern which allows components to share the states or their behaviors
with different components with the help of a function as a prop, thereby leading to greater
flexibility as well as reusability.
// A component that uses render props
function MouseTracker({ render }) {
const [coordinates, setCoordinates] = React.useState({ x: 0, y: 0 });
function handleMouseMove(event) {
setCoordinates({ x: event.clientX, y: event.clientY });
}
React.useEffect(() => {
window.addEventListener('mousemove', handleMouseMove);
return () => {
window.removeEventListener('mousemove', handleMouseMove);
};
}, []);
return <div>{render(coordinates)}</div>;
}
// A component that uses MouseTracker to get mouse position
function App() {
return (
<MouseTracker
render={({ x, y }) => (
<p>
Mouse position: ({x}, {y})
</p>
)}
/>
);
}
// sample component
function exampleComponent() {
return <div>My Content</div>;
}
// Usage
function App() {
return <MyComponentWithLoading isLoading={true} />;
}
3. Custom Hooks
These allow the extraction and reuse of logic in functional components as they encapsulate the stateful
logic and use in different components. Examples: useState, useEffect.
import React, { useState, useEffect } from 'react';
useEffect(() => {
const handleResize = () => {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
return windowSize;
// Example component using the custom hook
function App() {
const { width, height } = useWindowSize();
return (
<div>
<h1>Window Size</h1>
<p>Width: {width}px</p>
<p>Height: {height}px</p>
</div>
);
}
● The component renders the width and height in <p> tags and updates
automatically on window resize.
4. Context API
The Context API allows the sharing of values across different components without the
requirement of prop drilling. It helps in global state management.
It's useful when you have data that needs to be accessible by many components at different
nesting levels, such as:
● Theme
● Language preferences
● Global settings
Key Concepts of Context API
return (
<ThemeContext.Provider value={{ isDark, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
// Step 3: Create Components that consume the context
function ThemeToggleButton() {
const { isDark, toggleTheme } = useContext(ThemeContext); // Consume the context
return (
<button onClick={toggleTheme}>
Switch to {isDark ? 'Light' : 'Dark'} Theme
</button>
);
}
function ThemeDisplay() {
const { isDark } = useContext(ThemeContext); // Consume the context
return (
<div>
<h1>{isDark ? 'Dark Theme' : 'Light Theme'}</h1>
</div>
);
}
// App component using the ThemeProvider to wrap the application
function App() {
return (
<ThemeProvider>
<div style={{ padding: '20px' }}>
<ThemeDisplay />
<ThemeToggleButton />
</div>
</ThemeProvider>
);
}
● We create a context through React.createContext(), which returns an object with Provider and Consumer.
● ThemeProvider holds the state and toggling logic.
● ThemeToggleButton and ThemeDisplay use useContext to access the current theme value.
📦 Compound Components
Compound Components involve creating sets of components that work together with shared state. This improves flexibility and encapsulation.
● The parent component manages the shared state and passes props to its children.
✅ Example:
function handleClick(index) {
setOpenIndex(openIndex === index ? null : index);
}
return (
<div>
{React.Children.map(children, (child, index) => {
return React.cloneElement(child, {
isOpen: index === openIndex,
onClick: () => handleClick(index),
});
})}
</div>
);
}
// Child components
function AccordionItem({ isOpen, onClick, children }) {
return (
<div>
<button onClick={onClick}>{children}</button>
{isOpen && <div>Content of {children}</div>}
</div>
);
}
// Usage
function App() {
return (
<Accordion>
<AccordionItem>Item 1</AccordionItem>
<AccordionItem>Item 2</AccordionItem>
<AccordionItem>Item 3</AccordionItem>
</Accordion>
);
}
Example:
const Button = () => {
const inlineCSSbuttonStyle = {
backgroundColor: 'red',
color: 'black',
padding: '5px 10px',
};
3.Plain CSS
CSS stylesheets is a common way to style components in React. This involves the traditional approach of
creating a specific CSS file which is to be imported to the component you wish to use it in.
Example: we define style.css to apply custom style to a button:
/* plainCSS.css */
.button {background-color: black;
color: red;
padding: 5px 10px;
border: none;
}
.button:hover {
background-color: white;
}
Now we import above defined file in the required component using: import ‘./plainCSS.css’
and using the related style attribute as: <button className="button">PlainCSS method</button>;