Core Web Development Concepts: Node.js, Express, React, Redux, MongoDB
Classified in Computers
Written on in English with a size of 38.95 KB
Node.js Fundamentals
Understanding Node.js Modules
Modules in Node.js are reusable pieces of code that can be exported from one file and imported into another. Each file in Node.js is considered a module, operating within its own scope. Modules help organize code into separate files for better maintainability and reusability.
Types of Node.js Modules:
- Core Modules: Built-in modules provided by Node.js, pre-installed and ready to use.
- Local Modules: Custom modules created by developers for specific application logic.
- Third-party Modules: Modules installed via npm (Node Package Manager), extending Node.js functionality.
Key Built-in Node.js Core Modules
Core modules are essential, pre-installed components of Node.js. You can use them directly in your applications without any installation. Here are some common core modules:
- HTTP: For building web servers and handling HTTP requests and responses.
const http = require('http'); const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('Hello, Node.js!'); }); server.listen(3000, () => { console.log('Server running on http://localhost:3000'); });
This example uses the
http
module to create a simple server that responds with "Hello, Node.js!" when accessed. - FS (File System): Provides functions to interact with the file system, such as reading, writing, and deleting files.
const fs = require('fs'); fs.writeFileSync('example.txt', 'Hello Node.js!'); console.log('File "example.txt" created.');
- Path: For handling and transforming file and directory paths, ensuring cross-platform compatibility.
const path = require('path'); const filePath = path.join(__dirname, 'data', 'example.txt'); console.log(`Constructed file path: ${filePath}`);
- Events: Implements the observer pattern, allowing you to work with event emitters and listeners.
const EventEmitter = require('events'); const emitter = new EventEmitter(); emitter.on('greet', () => console.log('Hello from event!')); emitter.emit('greet');
- OS (Operating System): Provides information about the operating system on which Node.js is running.
const os = require('os'); console.log(`OS Platform: ${os.platform()}`); console.log(`CPU Cores: ${os.cpus().length}`);
Nodemon: Development Tool for Node.js
Nodemon is a utility that automatically restarts your Node.js application when file changes are detected during development. This significantly speeds up the development workflow by eliminating the need for manual restarts.
To install Nodemon:
npm install -g nodemon
To run your application with Nodemon:
nodemon app.js
Express.js Essentials
Routing in Express.js
Routing in Express.js is the process of defining how an application responds to client requests to specific endpoints (URLs) and HTTP methods (GET, POST, PUT, DELETE, etc.). Express.js simplifies this by providing methods to define routes and their corresponding handlers.
Key Concepts of Express.js Routing:
- Route Path: The URL pattern that a request must match (e.g.,
/users
,/products/:id
). - HTTP Method: The type of HTTP request (e.g.,
GET
for fetching,POST
for creating). - Route Handler: A function that executes when a route is matched, taking
req
(request),res
(response), andnext
(next middleware) as arguments.
Example: Defining Multiple Routes
const express = require('express');
const app = express();
const PORT = 3000;
// GET route for the Home Page
app.get('/', (req, res) => {
res.send('Welcome to the Home Page!');
});
// GET route for the About Page
app.get('/about', (req, res) => {
res.send('This is the About Page.');
});
// POST route for a Contact Form submission
app.post('/contact', (req, res) => {
res.send('Contact form submitted successfully!');
});
app.listen(PORT, () => {
console.log(`Express server running on http://localhost:${PORT}`);
});
In this example, app.get()
defines routes for GET requests, and app.post()
defines a route for POST requests. These routes handle different URLs and send appropriate responses.
Middleware in Express.js
In Express.js, middleware functions are executed in the application's request-response cycle. They have access to the request object (req
), the response object (res
), and the next()
function, which passes control to the next middleware function or route handler.
Capabilities of Middleware:
- Executing any code.
- Making changes to the request and response objects.
- Ending the request-response cycle.
- Calling the next middleware in the stack.
Types of Express.js Middleware:
- Application-level Middleware: Bound to an instance of the
app
object usingapp.use()
orapp.METHOD()
. - Router-level Middleware: Works with instances of
express.Router()
for modular routing. - Error-handling Middleware: Functions with four arguments (
err, req, res, next
) specifically for handling errors. - Built-in Middleware: Functions like
express.static()
for serving static files, andexpress.json()
/express.urlencoded()
for parsing request bodies. - Third-party Middleware: External packages like
cors
or older versions ofbody-parser
.
Middleware Syntax and Example:
// Basic middleware syntax
app.use((req, res, next) => {
console.log('Middleware executed!');
next(); // Pass control to the next middleware or route handler
});
Example: Logging Middleware
const express = require('express');
const app = express();
const PORT = 3000;
// Custom logging middleware
const requestLogger = (req, res, next) => {
console.log(`${req.method} ${req.url} at ${new Date().toISOString()}`);
next(); // Crucial to call next()
};
// Apply the middleware globally to all routes
app.use(requestLogger);
app.get('/', (req, res) => {
res.send('Home Page with Logging');
});
app.get('/about', (req, res) => {
res.send('About Page with Logging');
});
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
In this example, the requestLogger
middleware logs details of each incoming request before it reaches the route handler. The next()
function ensures the request continues its journey through the middleware stack.
Body Parsing in Express.js
The body-parser
module (or its built-in Express equivalents) is used to parse the body of incoming HTTP requests, making it accessible on the req.body
property. This is essential for handling data sent in POST or PUT requests, such as form submissions or JSON payloads.
Main Types of Body Parsing:
urlencoded
: Parses URL-encoded data, typically from HTML form submissions (e.g.,Content-Type: application/x-www-form-urlencoded
).json
: Parses JSON data sent in the request body (e.g.,Content-Type: application/json
).
Example: Using Express's Built-in Body Parsers
As of Express 4.16.0, body-parser
is no longer required as its core functionality is included directly in Express. You can use express.json()
and express.urlencoded()
:
const express = require('express');
const app = express();
const PORT = 3000;
// Middleware to parse JSON data
app.use(express.json());
// Middleware to parse URL-encoded data
app.use(express.urlencoded({ extended: true }));
// Route to handle POST request with JSON data
app.post('/submit-json', (req, res) => {
const data = req.body; // Accessing the parsed JSON data
res.send(`JSON data received: ${JSON.stringify(data)}`);
});
// Route to handle POST request with URL-encoded form data
app.post('/submit-form', (req, res) => {
const formData = req.body; // Accessing the parsed form data
res.send(`Form data received: Name: ${formData.name}, Email: ${formData.email}`);
});
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
In this example, express.json()
parses JSON payloads, and express.urlencoded()
handles URL-encoded form data, making it easy to access request body content via req.body
.
Web Services & Data Formats
Understanding APIs: How They Work
An API (Application Programming Interface) acts as a bridge, enabling different software systems to communicate and interact with each other. It defines a set of rules, protocols, and tools for building software applications and specifying how they should interact.
How an API Works:
- Request: A client (e.g., a web browser, mobile app, or another server) sends a request to the server via the API. This request specifies what action the client wants to perform (e.g., get data, create a resource).
- Processing: The server receives the request, processes it (which might involve interacting with a database, performing calculations, or calling other services), and prepares a response.
- Response: The server sends the result back to the client, typically in a standardized format like JSON or XML.
Example: Fetching User Data via API
- Client Request:
GET /api/users/123
(Client asks for user with ID 123) - Server Response (JSON):
{ "id": 123, "name": "Alice", "email": "[email protected]" }
Common Types of APIs:
- REST APIs: Widely used, relying on HTTP methods (GET, POST, PUT, DELETE) for communication.
- GraphQL APIs: Allow clients to request exactly the data they need, reducing over-fetching.
- SOAP APIs: Older, XML-based protocol, often used in enterprise environments.
REST API Architecture & HTTP Methods
REST (Representational State Transfer) is an architectural style for designing networked applications. A REST API is a web service built on REST principles, allowing client-server communication primarily over the HTTP protocol. REST APIs are stateless, meaning each request from the client to the server must contain all necessary information for the server to understand and process it, without relying on prior session information.
Key Principles of REST:
- Statelessness: Each request is independent; the server does not store client session context between requests.
- Client-Server Separation: Client and server are decoupled, allowing independent development and scaling.
- Uniform Interface: A consistent way to interact with resources, simplifying system architecture.
- Resource-Based: Everything is treated as a resource, identified by a URI (Uniform Resource Identifier).
- Representation: Resources are represented in formats like JSON or XML.
Common HTTP Methods in RESTful APIs:
- GET: Used to retrieve data from the server. It should be idempotent and safe (no side effects).
- Example:
GET /users
(Fetches a list of all users) - Response (JSON):
[ { "id": 1, "name": "Alice" }, { "id": 2, "name": "Bob" } ]
- Example:
- POST: Used to send data to the server to create a new resource.
- Example:
POST /users
(Creates a new user) - Request Body:
{ "name": "Charlie", "email": "[email protected]" }
- Response: Typically returns the newly created resource with its ID.
- Example:
- PUT: Used to update an existing resource completely. It replaces the entire resource with the new data.
- Example:
PUT /users/1
(Updates user with ID 1) - Request Body:
{ "name": "Alice Updated", "email": "[email protected]" }
- Response: Often a success message or the updated resource.
- Example:
- PATCH: Used to partially update an existing resource. Only the specified fields are modified.
- Example:
PATCH /users/1
(Updates only the email of user with ID 1) - Request Body:
{ "email": "[email protected]" }
- Example:
- DELETE: Used to remove a resource from the server.
- Example:
DELETE /users/1
(Deletes user with ID 1) - Response: Often a success message indicating deletion.
- Example:
REST APIs are widely adopted in modern web development due to their simplicity, scalability, and ease of integration across various platforms.
JSON: Data Format & Parsing
JSON (JavaScript Object Notation) is a lightweight, human-readable data interchange format. It is used to store and transport data, commonly between a server and a web application. JSON is easy for humans to read and write, and easy for machines to parse and generate.
JSON Structure Example:
{
"name": "Alice",
"age": 30,
"email": "[email protected]",
"isActive": true,
"hobbies": ["reading", "traveling"],
"address": {
"city": "New York",
"zip": "10001"
}
}
JSON data is represented as key-value pairs, similar to JavaScript objects. It supports various data types: strings, numbers, booleans, null, arrays, and nested objects.
What is JSON Parsing?
JSON parsing is the process of converting a JSON string into a native JavaScript object (or equivalent data structure in other programming languages). This allows you to access and manipulate the data programmatically.
Example: JSON Parsing in JavaScript/Node.js
// JSON String received from an API or file
const jsonString = '{"productName": "Laptop", "price": 1200, "inStock": true}';
// Parsing JSON String to a JavaScript Object
const product = JSON.parse(jsonString);
console.log(product.productName); // Output: Laptop
console.log(product.price); // Output: 1200
// Example of reading from a file in Node.js
const fs = require('fs');
try {
const fileData = fs.readFileSync('config.json', 'utf-8');
const config = JSON.parse(fileData);
console.log('Config loaded:', config);
} catch (error) {
console.error('Error reading or parsing config file:', error.message);
}
Converting Object to JSON (Stringify):
The reverse process, JSON stringification, converts a JavaScript object into a JSON string. This is typically done when sending data to a server or saving it to a file.
const userProfile = {
name: "Bob",
age: 25,
city: "London"
};
const jsonOutput = JSON.stringify(userProfile);
console.log(jsonOutput); // Output: {"name":"Bob","age":25,"city":"London"}
Database Concepts: MongoDB
What is MongoDB?
MongoDB is a popular NoSQL, document-oriented database system. Unlike traditional relational databases, MongoDB stores data in flexible, JSON-like documents called BSON (Binary JSON). This schema-less approach makes it highly scalable and adaptable for modern web applications that require dynamic data structures.
Key Concepts in MongoDB:
Database:
A database in MongoDB is a physical container for collections. It serves as a top-level organizational unit, similar to a database in SQL. You can have multiple databases on a single MongoDB server.
use myApplicationDB
This command switches to
myApplicationDB
. If it doesn't exist, MongoDB creates it when you first insert data into a collection within it.Collection:
A collection is a group of documents within a database. It is analogous to a "table" in relational databases, but with a key difference: collections do not enforce a fixed schema. Documents within the same collection can have different fields and structures.
db.createCollection("users")
Alternatively, a collection is automatically created when you insert the first document into it:
db.users.insertOne({ name: "Alice", age: 30 })
Document:
A document is a single record in MongoDB, stored in a BSON (JSON-like) format. Documents are the basic unit of data in MongoDB. Each document has a unique
_id
field, which acts as the primary key.{ "_id": ObjectId("60d5ec49bcf86cd799439011"), "title": "The Great Gatsby", "author": "F. Scott Fitzgerald", "year": 1925, "genres": ["classic", "fiction"] }
Documents can contain nested documents and arrays, providing a rich and flexible data model.
Essential MongoDB Shell Commands
The MongoDB shell (mongosh
) is a powerful command-line interface for interacting with your MongoDB database. Here are some fundamental commands:
Create or Switch Database:
use myNewDatabase
This command switches your current context to
myNewDatabase
. If the database does not exist, MongoDB creates it implicitly when you first save data to a collection within it.Create a Collection:
db.createCollection("products")
Explicitly creates a collection named
products
. As mentioned, collections are also created automatically upon the first document insertion.Insert Documents:
db.products.insertOne({ name: "Laptop", price: 1200, category: "Electronics" }) db.products.insertMany([ { name: "Keyboard", price: 75, category: "Electronics" }, { name: "Mouse", price: 25, category: "Electronics" } ])
insertOne()
adds a single document, whileinsertMany()
adds multiple documents to a collection.Find Documents:
db.products.find()
Retrieves all documents from the
products
collection.db.products.find().pretty()
Displays the results in a formatted, readable way.
db.products.find({ category: "Electronics", price: { $gt: 100 } })
Finds documents matching specific criteria (e.g., products in 'Electronics' category with price greater than 100).
Update Documents:
db.products.updateOne( { name: "Laptop" }, { $set: { price: 1150, inStock: true } } ) db.products.updateMany( { category: "Electronics" }, { $inc: { quantity: 10 } } )
updateOne()
updates the first document matching the filter;updateMany()
updates all matching documents.Delete Documents:
db.products.deleteOne({ name: "Mouse" })
Deletes the first document that matches the specified query.
db.products.deleteMany({ category: "Electronics", inStock: false })
Deletes all documents that match the query.
Show Collections:
show collections
Lists all collections present in the currently active database.
Show Databases:
show dbs
Lists all databases on the MongoDB server.
React & Redux Core Concepts
React Components: Functional vs. Class
In React, components are the fundamental building blocks for creating user interfaces (UIs). They are reusable, self-contained pieces of code that define how a part of the UI should look and behave. Components typically return React elements (often written using JSX) to describe what should be rendered.
1. Functional Components:
- Functional components are plain JavaScript functions that accept a single
props
object as an argument and return JSX. - They are generally simpler, more concise, and easier to test.
- With the introduction of React Hooks (e.g.,
useState
,useEffect
), functional components gained the ability to manage state and side effects, making them the preferred choice in modern React development.
Example: Functional Counter Component
import React, { useState } from 'react';
function Counter() {
// useState hook initializes 'count' state with 0 and provides 'setCount' function
const [count, setCount] = useState(0);
return (
Count: {count}
setCount(count + 1)}>Increment Count
);
}export default Counter;
This example demonstrates how useState(0)
initializes a state variable count
, and setCount
is used to update its value when the button is clicked.
2. Class Components:
- Class components are ES6 classes that extend
React.Component
. - They manage state using
this.state
and update it withthis.setState()
. - Class components also have lifecycle methods (e.g.,
componentDidMount
,componentDidUpdate
) for handling side effects.
Example: Class Counter Component
import React, { Component } from 'react';
class Counter extends Component {
constructor(props) {
super(props);
this.state = { count: 0 }; // Initialize state in the constructor
}
increment = () => {
this.setState({ count: this.state.count + 1 }); // Update state using setState
};
render() {
return (
Count: {this.state.count}
Increment Count
);
}
}export default Counter;
Here, state is initialized in the constructor, and this.setState()
is used to update the count
value, triggering a re-render.
Key Differences & Modern Usage:
While class components are still valid and found in legacy codebases, functional components with Hooks are now the standard for new React development due to their simplicity, better readability, and improved reusability of logic.
Redux State Immutability
In Redux, the application state is strictly immutable. This means that you should never directly modify the existing state object. Instead, whenever a state update is needed, you must create and return a new state object with the desired changes.
Why Immutability is Crucial in Redux:
- Predictability: Immutability ensures that state changes are explicit and traceable, making it easier to understand how your application's state evolves.
- Time Travel Debugging: Tools like Redux DevTools can replay actions and revert state, which relies on the immutability of state snapshots.
- Pure Functions: Reducers, which are responsible for updating state, must be pure functions. Pure functions do not cause side effects and always return the same output for the same input, which is only possible if they don't mutate their arguments (the state).
- Performance Optimization: React and Redux can perform shallow comparisons of state objects to determine if a re-render is necessary. If state objects are mutated directly, these comparisons might fail to detect changes, leading to bugs or inefficient updates.
Example: Immutable State Update in Redux
const initialState = { count: 0, user: { name: 'Guest' } };
// Correct Reducer (Immutable Update)
const counterReducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
// Returns a NEW state object with updated count
return { ...state, count: state.count + 1 };
case 'UPDATE_USER_NAME':
// Deep copy for nested objects to maintain immutability
return {
...state,
user: { ...state.user, name: action.payload }
};
default:
return state;
}
};
// Simulating state changes
let currentState = counterReducer(undefined, {});
console.log('Initial State:', currentState); // { count: 0, user: { name: 'Guest' } }
currentState = counterReducer(currentState, { type: 'INCREMENT' });
console.log('After Increment:', currentState); // { count: 1, user: { name: 'Guest' } }
currentState = counterReducer(currentState, { type: 'UPDATE_USER_NAME', payload: 'Alice' });
console.log('After User Update:', currentState); // { count: 1, user: { name: 'Alice' } }
In the example above, the spread syntax (...state
) creates a shallow copy of the current state, and then specific properties are updated. For nested objects, a deep copy (or at least a shallow copy of the nested object) is necessary to ensure full immutability.
Incorrect (Mutable) Update:
case 'INCREMENT':
state.count += 1; // Direct mutation - AVOID THIS!
return state;
Directly mutating state.count
would violate Redux's principles and can lead to unpredictable behavior and debugging challenges.
Redux Thunk Middleware
Redux Thunk is a popular middleware for Redux that allows you to write action creators that return a function instead of a plain action object. This is particularly useful for handling asynchronous logic, such as making API calls, before dispatching a final action to the reducer.
Why Use Redux Thunk?
- Asynchronous Operations: Standard Redux actions must be plain objects. Thunk enables you to dispatch functions that can perform async operations (like
fetch
oraxios
requests) and then dispatch regular actions based on the results (e.g.,FETCH_SUCCESS
,FETCH_ERROR
). - Access to
dispatch
andgetState
: The function returned by a Thunk action creator receivesdispatch
andgetState
as arguments, allowing you to dispatch multiple actions at different stages of an async operation or access the current state.
Example: Fetching Data with Redux Thunk
import { createStore, applyMiddleware } from 'redux';
import { thunk } from 'redux-thunk'; // Correct import for modern Redux Toolkit setup or standalone thunk
// Initial State
const initialState = {
data: [],
loading: false,
error: null
};
// Reducer
const dataReducer = (state = initialState, action) => {
switch (action.type) {
case 'FETCH_REQUEST':
return { ...state, loading: true, error: null };
case 'FETCH_SUCCESS':
return { ...state, loading: false, data: action.payload };
case 'FETCH_ERROR':
return { ...state, loading: false, error: action.error };
default:
return state;
}
};
// Action Creator (using Redux Thunk)
const fetchData = () => {
return async (dispatch) => {
dispatch({ type: 'FETCH_REQUEST' }); // Dispatch action to indicate loading
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1'); // Example API call
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
dispatch({ type: 'FETCH_SUCCESS', payload: data }); // Dispatch success with data
} catch (error) {
dispatch({ type: 'FETCH_ERROR', error: error.message }); // Dispatch error
}
};
};
// Create Store with Thunk middleware
const store = createStore(dataReducer, applyMiddleware(thunk));
// Dispatch the Thunk action
store.dispatch(fetchData());
// You would typically connect this to a React component to display loading, data, or error states.
// For demonstration, we can log state changes (in a real app, use Redux DevTools)
store.subscribe(() => console.log('Current State:', store.getState()));
In this example, fetchData
is a Thunk action creator. It dispatches a FETCH_REQUEST
action immediately, performs an asynchronous API call, and then dispatches either a FETCH_SUCCESS
or FETCH_ERROR
action based on the outcome. This pattern effectively manages the lifecycle of an asynchronous operation within Redux.
Redux Application Data Flow
Redux enforces a strict unidirectional data flow, which makes state management predictable and easier to debug. The data flow follows a clear cycle:
UI Component Interaction:
A user interacts with the UI (e.g., clicks a button). This interaction triggers an event that needs to update the application state.
Dispatching an Action:
The UI component dispatches an Action. An action is a plain JavaScript object that describes what happened (e.g.,
{ type: 'INCREMENT_COUNTER' }
). It's the only way to trigger a state change.Middleware (Optional):
Before reaching the reducer, the action might pass through Middleware (like Redux Thunk or Saga). Middleware can intercept, modify, or delay actions, especially for handling side effects like API calls or logging.
Reducer Processing:
The action (and the current state) is sent to the Reducer. A reducer is a pure function that takes the current state and an action, and returns a new state based on the action's type. It never mutates the original state.
Store Update:
The new state returned by the reducer updates the Store. The store is the single source of truth for the entire application's state.
View Re-rendering:
Any UI components that are "connected" or "subscribed" to the store will detect the state change. React components, for instance, will re-render themselves with the new data from the store, reflecting the updated state in the UI.
Redux Data Flow Diagram:
+-----------------+ Action +-----------------+
| UI Component | -------------> | Store |
+-----------------+ +-----------------+
^ |
| State Update (Re-render) | Dispatch
| |
+-----------------+ <------------------ +-----------------+
| View | New State | Reducer |
+-----------------+ +-----------------+
^
| (Current State, Action)
|
Example: Simple Redux Data Flow
import { createStore } from 'redux';
// 1. Action: Describes what happened
const incrementCounter = () => ({
type: 'INCREMENT_COUNTER'
});
// 2. Reducer: Calculates new state based on action
const counterReducer = (state = { count: 0 }, action) => {
switch (action.type) {
case 'INCREMENT_COUNTER':
return { count: state.count + 1 }; // Return new state
default:
return state;
}
};
// 3. Store: Holds the application state
const store = createStore(counterReducer);
// 4. View (simulated): Subscribe to state changes
store.subscribe(() => {
console.log('Current State:', store.getState());
});
// 5. Dispatch Action from UI (simulated)
console.log('Dispatching INCREMENT_COUNTER...');
store.dispatch(incrementCounter()); // Output: Current State: { count: 1 }
console.log('Dispatching INCREMENT_COUNTER again...');
store.dispatch(incrementCounter()); // Output: Current State: { count: 2 }
This example illustrates how an action triggers a reducer, which updates the store, and then any subscribed parts of the UI can react to the new state.
Pure Functions Explained
A Pure Function is a fundamental concept in functional programming, and it's a core principle behind Redux reducers. A function is considered pure if it adheres to two main rules:
Deterministic (Same Input, Same Output):
Given the same input arguments, a pure function will always return the exact same output. It does not depend on any external state or mutable data.
No Side Effects:
A pure function does not cause any observable side effects. This means it does not:
- Modify any external variables or data structures (e.g., global variables, arguments passed by reference).
- Perform I/O operations (e.g., network requests, database calls, console logging).
- Modify the DOM.
- Generate random numbers or get the current time.
Example of a Pure Function:
// Pure Function: Always returns the same sum for the same numbers
function add(a, b) {
return a + b;
}
console.log(add(5, 3)); // Output: 8
console.log(add(5, 3)); // Output: 8 (Calling it again with same input yields same output)
Example of an Impure Function:
let total = 0; // External state
// Impure Function: Modifies external state (side effect)
function addToTotal(value) {
total += value; // Modifies 'total'
return total;
}
console.log(addToTotal(5)); // Output: 5 (total is now 5)
console.log(addToTotal(5)); // Output: 10 (total is now 10, different output for same input)
// Another Impure Function: Depends on external state (Date.now())
function generateTimestamp() {
return Date.now(); // Returns different value each time
}
console.log(generateTimestamp()); // Output: e.g., 1678886400000
console.log(generateTimestamp()); // Output: e.g., 1678886400001 (different output)
Pure Functions in Redux:
In Redux, reducers must be pure functions. They take the current state and an action as input, and they must return a new state object without modifying the original state or causing any other side effects. This purity is fundamental to Redux's predictability, testability, and time-travel debugging capabilities.
// Pure Reducer Example
const counterReducer = (state = 0, action) => {
switch (action.type) {
case 'INCREMENT':
return state + 1; // Returns a new value, doesn't modify 'state'
case 'DECREMENT':
return state - 1;
default:
return state;
}
};
By adhering to the pure function principle, Redux ensures that state changes are explicit, predictable, and easy to reason about.
Key Web Development Terminology
- API (Application Programming Interface)
- A set of rules and protocols that allows different software applications to communicate with each other.
- Callback Function
- A function passed as an argument to another function, which is then executed inside the outer function after some operation completes.
- Express.js
next()
- A function in Express.js middleware that, when called, passes control to the next middleware function in the application's request-response cycle.
- File System (
fs
) Module in Node.js - A core Node.js module that provides an API for interacting with the file system, enabling operations like reading, writing, and deleting files.
- JSON (JavaScript Object Notation)
- A lightweight, human-readable data interchange format used for storing and transporting data, commonly between a server and a web client.
- JSX (JavaScript XML)
- A syntax extension for JavaScript used in React to describe what the UI should look like. It allows writing HTML-like code directly within JavaScript.
- Middleware (Express.js)
- Functions that have access to the request object, the response object, and the next middleware function in the application's request-response cycle. They can modify requests/responses or end the cycle.
- MongoDB
- A NoSQL, document-oriented database that stores data in flexible, JSON-like documents (BSON), designed for scalability and high performance.
- Node.js
- A JavaScript runtime environment that allows developers to execute JavaScript code outside of a web browser, commonly used for building server-side applications.
- Nodemon
- A utility that automatically restarts a Node.js application when file changes are detected, streamlining the development process.
- Props in React
- Short for "properties," props are read-only inputs passed from a parent component to a child component in React, enabling data flow and customization.
- Pure Function
- A function that always produces the same output for the same input and has no side effects (does not modify external state).
- Redux
- A predictable state container for JavaScript applications, often used with React, that helps manage application state in a centralized store.
- Redux Thunk
- A Redux middleware that allows you to write action creators that return a function instead of a plain action object, enabling asynchronous logic.
- Representational State Transfer (REST)
- An architectural style for designing networked applications, emphasizing stateless client-server communication using standard HTTP methods.
res.send()
(Express.js)- An Express.js method used to send a response to the client and end the request-response cycle. It can send various types of data (string, object, array, buffer).
useState
Hook (React)- A React Hook that allows functional components to add state variables. It returns an array containing the current state value and a function to update it.