Class Components: These are written using JavaScript classes. They have their own state and can use lifecycle methods like componentDidMount. They use the 'this' keyword to access props and state.
Functional Components: These are written as regular JavaScript functions. They are simpler and easier to read. With React Hooks, they can now have state and lifecycle features too.
Key differences:
A PureComponent is a special type of component that automatically checks if it needs to re-render. It compares the old props and state with the new ones. If they're the same, it skips re-rendering to improve performance.
When to use PureComponent:
When to avoid PureComponent:
React Hooks are special functions that let you use React features in functional components. They let you "hook into" React's state and lifecycle features without writing a class.
Why hooks were created:
Common hooks:
DOM (Document Object Model): This is the actual structure of your webpage that the browser displays. Changing it directly can be slow.
Virtual DOM: This is React's copy of the DOM kept in memory. It's much faster to work with than the real DOM.
How it works:
Benefits:
Controlled Components: React controls the form input values. The input value is stored in React state, and you update it through React.
Uncontrolled Components: The browser controls the form input values. You use refs to get the values when you need them.
When to use controlled:
When to use uncontrolled:
A Higher-Order Component (HOC) is a function that takes a component and returns a new component with extra features. It's like a wrapper that adds functionality.
What HOCs do:
Common examples:
Modern alternative: Custom hooks are now preferred over HOCs because they're simpler and don't create wrapper components.
React Fragment lets you group multiple elements together without adding an extra div to the DOM. It's like an invisible wrapper.
Why use Fragment:
Two ways to write Fragment:
When you need Fragment:
Lazy loading in React is a performance optimization technique where components (or parts of your application) are loaded only when they are needed, instead of loading everything upfront when the application starts. This helps reduce the initial bundle size, improves page load times, and provides a smoother user experience, especially for large applications.
const MyComponent = React.lazy(() => import("./MyComponent"));
How it works:
Benefits:
What you need:
Best for: Different pages/routes, large components, features not immediately visible
import React, { Suspense } from "react";
const MyComponent = React.lazy(() => import("./MyComponent"));
function App() {
return (
<div>
<h1>Lazy Loading Example</h1>
<Suspense fallback={<p>Loading...</p>}>
<MyComponent />
</Suspense>
</div>
);
}
Stateless Components: These don't have their own state. They just receive props and display them. They're like pure functions - same input always gives same output.
Stateful Components: These have their own state that can change over time. They can remember things and update themselves.
Stateless characteristics:
Stateful characteristics:
When to use each:
Pointer events are a modern way to handle mouse, touch, and pen input all in one. React provides synthetic pointer events that work the same across all devices.
Common pointer events: onPointerDown (when you press), onPointerUp (when you release), onPointerMove (when you move), onPointerEnter (when you enter an area), onPointerLeave (when you exit an area).
Benefits: Works with mouse, touch, and stylus. Better than separate mouse and touch events.
Keys are special props that help React identify which items have changed in a list. Think of them like ID tags for list items.
Why keys matter: They help React update lists efficiently. Without keys, React might re-render the entire list when just one item changes.
Good keys: Use unique, stable IDs like database IDs. Bad keys: Don't use array indexes (can cause bugs when list order changes).
Rule: Each key should be unique among siblings, but can repeat in different lists.
Conditional Rendering: It means deciding which UI to show depending on certain conditions (e.g., if a user is logged in or not).
Methods:
{condition ? showThis : showThat}
{condition && showThis}
return
and decide what to render.Example in React:
function App() {
const isLoggedIn = true;
return (
<div>
{/* Ternary operator */}
<div>
{isLoggedIn ? <h2>Welcome Back!</h2> : <h2>Please Login</h2>}
</div>
{/* Logical AND */}
<div>
{isLoggedIn && <button>Logout</button>}
</div>
</div>
);
}
JSX is a syntax that lets you write HTML-like code inside JavaScript. It makes React components easier to read and write.
How it works: JSX gets converted into regular JavaScript function calls that create React elements.
Rules: Use className instead of class, close all tags, wrap multiple elements in one parent, use curly braces for JavaScript expressions.
Benefits: More readable than pure JavaScript, familiar HTML syntax, better error messages, good tooling support.
React applications are Single Page Applications (SPA). Instead of loading a new page from the server for every navigation, React Router updates the UI dynamically using the browser’s History API (pushState, replaceState, popstate). This allows smooth navigation without full page reloads.
Key Concepts:
/user/123
.?filter=active
)./dashboard/profile
.Example:
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/user/:id" element={<User />} />
</Routes>
</BrowserRouter>
Benefits:
In short: React Router turns your single-page React app into a multi-view experience, making navigation smooth, efficient, and user-friendly.
React.createElement: It’s a fundamental method in React used to create React elements. React elements are the building blocks of a React app, representing what should appear on the screen. React uses them in its virtual DOM to efficiently update and render the UI.
React.cloneElement: A method to copy an existing React element and optionally add or override props. Useful when you want to modify a child element without rewriting it.
In short:
createElement
→ Creates elements from JSX (React uses this internally).cloneElement
→ Copies an element and adds new props.Example in React:
// Using createElement (JSX is converted to this)
const element1 = React.createElement('h1', null, 'Hello World');
// Equivalent JSX
const element2 = <h1>Hello World</h1>;
// Using cloneElement to add props
function Wrapper({ children }) {
return React.cloneElement(children, { style: { color: 'blue' } });
}
function App() {
return (
<Wrapper>
<h2>This text will be blue</h2>
</Wrapper>
);
}
Definition: The children
prop contains whatever you place between the opening and closing tags of a component. Think of it as a special slot where content can be inserted.
Why it's useful: Makes components more flexible and reusable. You can create wrapper components like Card
, Modal
, or Button
that can hold different content.
Types of children: Can be text, React elements, other components, or even functions. React also provides React.Children
utilities to handle children safely.
Example:
function Card({ children }) {
return (
<div className="card">
<div className="card-body">
{children}
</div>
</div>
);
}
// Usage
function App() {
return (
<Card>
<h2>Hello World</h2>
<p>This is a card content.</p>
</Card>
);
}
Prototype is JavaScript's way of sharing properties and methods between objects. Every JavaScript object has a prototype - it's like a blueprint that objects inherit from.
How it works: When you try to access a property on an object, JavaScript first looks on the object itself. If not found, it looks up the prototype chain.
In React context:
Modern relevance: While prototypes are fundamental to JavaScript, React has moved toward functional components and hooks, reducing direct prototype usage.
Definition: Reconciliation is React’s process of updating the UI efficiently by comparing the new Virtual DOM with the previous one and applying only the necessary changes to the real DOM.
How it works: React performs a “diffing” algorithm on the Virtual DOM to detect what has changed, and updates only those parts of the DOM that actually need to change.
Key factors React checks:
Why it matters: Efficient updates improve performance, reduce unnecessary re-rendering, and make React applications faster.
Example:
function App() {
const [count, setCount] = React.useState(0);
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
// When 'count' changes, React updates only the <h1> text, not the entire DOM.
Definition: React Portals allow you to render a component's children into a DOM node that exists outside the DOM hierarchy of its parent component. Essentially, it lets you “teleport” part of your UI to a different place in the DOM.
Common Use Cases:
How it works: Even though the DOM elements are rendered elsewhere, the portal component remains part of the React component tree. This means React still manages its state, context, and event propagation as usual.
Benefits:
Example:
import React from 'react';
import ReactDOM from 'react-dom';
function Modal({ children }) {
return ReactDOM.createPortal(
<div className="modal">
{children}
</div>,
document.getElementById('modal-root') // renders outside parent DOM
);
}
function App() {
return (
<div>
<h1>Main App Content</h1>
<Modal>
<p>This is a modal rendered using a portal.</p>
</Modal>
</div>
);
}
Definition: Server-Side Rendering (SSR) is the process of generating the HTML of a React application on the server and sending it to the browser fully rendered, instead of letting the client build the HTML with JavaScript.
Benefits:
Challenges:
Popular tools/frameworks: Next.js (most popular), Gatsby, Remix — they simplify SSR implementation.
Example using Next.js:
// pages/index.js
export default function Home({ posts }) {
return (
<div>
<h1>Blog Posts</h1>
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
// This function runs on the server
export async function getServerSideProps() {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
return { props: { posts } };
}
Redux is a predictable state container library for JavaScript applications that provides a centralized store to manage the entire application's state, making state changes predictable and easy to track. It is used in larger applications to simplify state management, especially when data needs to be shared across many components. It follows the principle of unidirectional data flow.
Key Concepts:
{ type: "ADD_TODO", payload: "Learn Redux" }
).How Redux Works (Flow):
User → Dispatch(Action) → Reducer → New State → UI Updates
Steps to Use Redux (with Example):
npm install redux react-redux
// actions.js
export const addTodo = (task) => ({
type: "ADD_TODO",
payload: task
});
// reducers.js
const initialState = [];
export function todoReducer(state = initialState, action) {
switch (action.type) {
case "ADD_TODO":
return [...state, action.payload];
default:
return state;
}
}
// store.js
import { createStore } from "redux";
import { todoReducer } from "./reducers";
const store = createStore(todoReducer);
export default store;
// index.js
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import App from "./App";
import store from "./store";
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
// App.js
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { addTodo } from "./actions";
function App() {
const todos = useSelector(state => state);
const dispatch = useDispatch();
return (
<div>
<h3>Todo List</h3>
<button onClick={() => dispatch(addTodo("Learn Redux"))}>
Add Todo
</button>
<ul>
{todos.map((t, i) => <li key={i}>{t}</li>)}
</ul>
</div>
);
}
export default App;
When to Use Redux:
Modern Alternatives:
In short: Redux provides a predictable way to manage state by centralizing it in one store and ensuring a strict one-way data flow. This improves maintainability, scalability, and debugging in large applications.
The connect() function is a higher-order component (HOC) from
react-redux
that connects React components to the Redux store.
It was the traditional way of using Redux before useSelector
and useDispatch
hooks.
Example:
// Counter.js
import React from "react";
import { connect } from "react-redux";
function Counter({ count, dispatch }) {
return (
<div>
<h3>Count: {count}</h3>
<button onClick={() => dispatch({ type: "INCREMENT" })}>+</button>
</div>
);
}
const mapStateToProps = (state) => ({
count: state.count
});
export default connect(mapStateToProps)(Counter);
Modern Alternative: Prefer useSelector
and useDispatch
hooks.
Redux Thunk is a middleware that allows writing asynchronous logic in Redux. Without it, Redux only supports synchronous state updates.
Example:
// actions.js
export const fetchUsers = () => {
return async (dispatch) => {
dispatch({ type: "USERS_REQUEST" });
try {
const res = await fetch("https://jsonplaceholder.typicode.com/users");
const data = await res.json();
dispatch({ type: "USERS_SUCCESS", payload: data });
} catch (err) {
dispatch({ type: "USERS_FAILURE", error: err });
}
};
};
When to Use: API calls, async workflows, conditional logic.
Redux Saga is a middleware that handles asynchronous side effects
using generator functions
. It provides a more powerful way to manage complex async workflows compared to Redux Thunk.
Example:
// sagas.js
import { call, put, takeEvery } from "redux-saga/effects";
function* fetchUsers() {
try {
const res = yield call(fetch, "https://jsonplaceholder.typicode.com/users");
const data = yield res.json();
yield put({ type: "USERS_SUCCESS", payload: data });
} catch (err) {
yield put({ type: "USERS_FAILURE", error: err });
}
}
export function* watchUserRequests() {
yield takeEvery("USERS_REQUEST", fetchUsers);
}
When to Use: Complex async logic, race conditions, background tasks.
In Redux Toolkit (RTK), a slice represents a portion of the Redux store with its
reducer logic and actions bundled together using createSlice
.
Example:
// todoSlice.js
import { createSlice } from "@reduxjs/toolkit";
const todoSlice = createSlice({
name: "todos",
initialState: [],
reducers: {
addTodo: (state, action) => {
state.push(action.payload);
},
removeTodo: (state, action) => {
return state.filter((_, i) => i !== action.payload);
}
}
});
export const { addTodo, removeTodo } = todoSlice.actions;
export default todoSlice.reducer;
Benefits: Less boilerplate, easier to scale, built-in immutability with Immer.
Windowing (or virtualization) means only rendering the items that are currently visible on screen, not the entire list. It's like having a window that shows only part of a huge list.
Why it's needed: Rendering thousands of items at once can make your app slow and use too much memory.
How it works: As you scroll, items outside the visible area are removed from the DOM and new ones are added.
Popular libraries: react-window (lightweight), react-virtualized (feature-rich). These handle the complex calculations for you.
When to use: Lists with hundreds or thousands of items, data tables, infinite scroll feeds.
Flux is a pattern for managing data flow in applications. It enforces a one-way data flow, making apps more predictable and easier to debug.
Four main parts:
Key rule: Data flows in one direction: Actions → Dispatcher → Stores → Views.
Legacy note: Flux was mostly replaced by Redux, which simplified the pattern.
Flux: The original pattern with multiple stores, a dispatcher, and more boilerplate code. More complex but flexible.
Redux: Simplified version of Flux with one store, no dispatcher, and pure functions (reducers) for updates.
Key differences:
Result: Redux became more popular due to its simplicity and excellent developer experience.
Prop drilling happens when you pass props through many component layers just to get data to a deeply nested component. It's like passing a message through many people.
Problems: Makes components messy, harder to maintain, and couples components that shouldn't be connected.
Solutions:
When it's okay: For 2-3 component levels, prop drilling is fine and actually clearer than alternatives.
useLayoutEffect is like useEffect, but it runs before the browser paints the screen. It's useful when you need to make DOM changes that users shouldn't see.
Difference from useEffect: useLayoutEffect runs synchronously before painting, useEffect runs after painting.
When to use useLayoutEffect:
Warning: Can hurt performance if overused because it blocks painting. Prefer useEffect unless you specifically need synchronous execution.
React Component Lifecycle refers to the series of events (mounting, updating, unmounting) that happen from the creation of a component to its removal from the DOM. Lifecycle methods (in class components) or hooks (in functional components) allow developers to run code at specific points in this process.
Main Lifecycle Phases:
props
or state
change.
Class Component Lifecycle Methods:
constructor()
→ Initialization (state, bindings)componentDidMount()
→ Runs after first render (good for API calls, subscriptions)shouldComponentUpdate()
→ Controls whether re-render should happencomponentDidUpdate(prevProps, prevState)
→ Runs after every update (good for reacting to changes)componentWillUnmount()
→ Cleanup (remove listeners, cancel requests)Functional Component Lifecycle (using Hooks):
In functional components, useEffect
hook is used to handle all lifecycle phases.
// Mounting (componentDidMount)
useEffect(() => {
console.log("Mounted");
}, []);
// Updating (componentDidUpdate)
useEffect(() => {
console.log("Updated when count changes");
}, [count]);
// Unmounting (componentWillUnmount)
useEffect(() => {
const id = setInterval(() => console.log("Running..."), 1000);
return () => clearInterval(id); // cleanup
}, []);
Summary:
useEffect
, useLayoutEffect
) for all lifecycle needs.✅ Modern React Best Practice: Prefer functional components with hooks since they are cleaner, easier to reuse, and encouraged in React’s latest guidelines.
React uses one-way data flow (also called unidirectional data flow). Data flows down from parent to child components through props, like water flowing downhill.
Key principles:
Communication patterns: Parent to child (props), child to parent (callback functions), sibling to sibling (lift state up to common parent).
Benefits: Easier to understand, fewer bugs, better performance optimizations possible.
Main styling approaches:
Best practices: Use className for static styles, inline styles for dynamic values, CSS Modules or CSS-in-JS for component-scoped styles.
Remember: Use className instead of class in JSX, and style prop expects an object, not a string.
React state updates are asynchronous and batched for performance. React groups multiple state updates together and applies them all at once.
Key behaviors:
Best practices: Don't rely on state values immediately after setting them, use useEffect to respond to state changes, prefer functional updates when new state depends on old state.
Internationalization (i18n) means making your app work in different languages and regions. It involves translating text, formatting dates/numbers, and handling different text directions.
What you need to handle:
Popular libraries: react-i18next (most popular), React Intl, LinguiJS.
Best practices: Extract all text to translation files, use translation keys, test with longer languages, consider cultural differences beyond language.
Dynamic imports let you load JavaScript modules on-demand instead of loading everything upfront. This makes your initial app bundle smaller and faster to load.
How it works: Instead of importing at the top of the file, you import when you actually need the code (like when user clicks a button or navigates to a page).
Common uses:
React integration: Use React.lazy() with dynamic imports and Suspense to handle loading states gracefully.
Benefits: Smaller initial bundle, faster startup, better user experience, reduced bandwidth usage.
React provides Refs and memoization utilities to help with performance optimization and DOM manipulation.
useRef
is a React hook that creates a mutable reference object which persists across renders.
Changing its .current
value does not trigger re-renders.
Common use cases:
// Example: Accessing DOM element
import React, { useRef, useEffect } from "react";
function InputFocus() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus(); // Focus input on mount
}, []);
return <input ref={inputRef} placeholder="Type here..." />;
}
React.memo
is a higher-order component (HOC) that memoizes
a functional component. It prevents unnecessary re-renders when the component’s
props have not changed.
When to use React.memo:
// Example: Memoized Component
import React from "react";
const Child = React.memo(({ value }) => {
console.log("Child rendered");
return <p>Value: {value}</p>;
});
function Parent() {
const [count, setCount] = React.useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>Increment</button>
<Child value="Hello" /> {/* Won't re-render unless "value" changes */}
</div>
);
}
✅ Best Practice: Use useRef
for DOM/timers/stateful refs and
React.memo
for performance optimization. Don’t overuse memoization; apply it only when needed.
Redux flow in React involves actions, reducers, store, and components to manage state in a centralized manner.
JSX is a syntax extension for JavaScript used in React to write HTML-like code within JavaScript.
useMemo: A hook that memoizes the result of a function and returns the cached value on subsequent renders.
Example: useMemo(() => calculateExpensiveValue(a, b), [a, b])
A single-page application (SPA) is a web app that loads once and dynamically updates content without refreshing the page.
React provides efficient and flexible UI rendering, component reusability, and easy integration with other libraries.
useSelector: A React-Redux hook used to extract data from the Redux store state.
Example:
const user = useSelector((state) => state.user)
CSS Box Model: Every HTML element is considered a box with the following parts:
CSS Selectors: Patterns used to select elements for styling.
div { }
.classname { }
#id { }
[type="text"] { }
a:hover { }
p::first-letter { }
Debouncing: Ensures that a function is executed only after a specified delay has passed since the last time it was invoked. Useful for actions like search input, resize, or keypress events.
function debounce(fn, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
Throttling: Ensures that a function is executed at most once in a specified interval, no matter how many times the event is triggered. Useful for scroll, resize, or mouse move events.
function throttle(fn, limit) {
let lastCall = 0;
return function(...args) {
const now = Date.now();
if (now - lastCall >= limit) {
lastCall = now;
fn.apply(this, args);
}
};
}
Definition: PropTypes is a built-in type checking feature in React that lets you specify the types of props a component should receive. It helps catch bugs by ensuring that components get the correct prop types.
Why it's useful:
Common PropTypes:
PropTypes.string
PropTypes.number
PropTypes.bool
PropTypes.array
PropTypes.object
PropTypes.func
PropTypes.node
(anything that can be rendered: numbers, strings, elements, arrays, or fragments)PropTypes.element
(a React element)PropTypes.any
(any type)Example:
import PropTypes from 'prop-types';
function Button({ label, onClick, disabled }) {
return (
<button onClick={onClick} disabled={disabled}>
{label}
</button>
);
}
// Define prop types
Button.propTypes = {
label: PropTypes.string.isRequired,
onClick: PropTypes.func,
disabled: PropTypes.bool
};
// Default props
Button.defaultProps = {
onClick: () => {},
disabled: false
};
Definition: Testing in React means verifying that components, logic, and UI behave as expected. It helps catch bugs early and makes apps more reliable.
Types of testing in React:
Popular tools:
Jest
→ JavaScript testing framework (comes with Create React App).React Testing Library (RTL)
→ For testing components in a way closer to how users interact with them.Cypress / Playwright
→ For end-to-end browser testing.Example with React Testing Library:
import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';
test('button click updates text', () => {
render(<Button />);
const btn = screen.getByRole('button');
fireEvent.click(btn);
expect(btn).toHaveTextContent('Clicked');
});
Best Practice: Test behavior (what the user sees and does), not implementation details.
Definition: Styled Components is a popular library for React that lets you write CSS directly in JavaScript files using a technique called CSS-in-JS. It allows you to create styled React components with scoped styles.
Why use it?
Example:
import styled from 'styled-components';
// Create a styled button
const Button = styled.button`
background: ${props => props.primary ? "blue" : "gray"};
color: white;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
`;
function App() {
return (
<div>
<Button>Normal Button</Button>
<Button primary>Primary Button</Button>
</div>
);
}
Best Practice: Use styled-components for component-level styles and avoid mixing too much global CSS.