Top Interview Questions
React is a popular open-source JavaScript library used for building user interfaces, especially single-page applications (SPAs). Developed and maintained by Facebook (now Meta) and a large open-source community, React has become one of the most widely used frontend technologies in modern web development. Its component-based architecture, performance optimizations, and flexibility make it a preferred choice for developers building scalable and maintainable applications.
React was first released in 2013 by Facebook to solve the problem of efficiently updating and rendering dynamic user interfaces. Traditional approaches using direct DOM manipulation were slow and difficult to manage as applications grew in size and complexity. React introduced a declarative programming model, where developers describe what the UI should look like for a given state, and React takes care of updating the UI when the state changes.
At its core, React focuses only on the view layer of an application. Unlike full-fledged frameworks such as Angular, React is a library that can be combined with other tools and libraries to build complete applications. This flexibility allows developers to choose routing, state management, and data-fetching solutions according to project needs.
One of React’s most important concepts is the component-based architecture. A component is a reusable, self-contained piece of UI that defines how a part of the interface should appear and behave. Components can be as simple as a button or as complex as an entire page.
React supports two main types of components:
Functional components, which are JavaScript functions that return JSX.
Class components, which are ES6 classes extending React.Component (less common today).
With the introduction of React Hooks, functional components have become the standard way of writing React code. Hooks allow developers to manage state, lifecycle methods, and side effects without using class components. This has simplified React development and improved code readability and reusability.
React uses JSX (JavaScript XML), a syntax extension that allows developers to write HTML-like code inside JavaScript. JSX makes UI code more readable and expressive by keeping structure and logic together.
Although JSX looks like HTML, it is actually compiled into JavaScript function calls. This allows React to create lightweight JavaScript objects representing UI elements. By combining JavaScript logic and UI markup, developers can easily build dynamic and interactive interfaces.
One of the key reasons behind React’s performance is the Virtual DOM. Instead of directly updating the real DOM every time the application state changes, React creates a virtual representation of the DOM in memory. When a change occurs, React compares the new virtual DOM with the previous one using a process called diffing.
After identifying the minimal set of changes, React updates only the necessary parts of the real DOM. This approach significantly improves performance, especially in applications with frequent UI updates, such as dashboards, real-time applications, and complex forms.
React applications rely heavily on state and props to manage data and control UI behavior.
Props (properties) are read-only values passed from a parent component to a child component. They allow data to flow in one direction, making the application easier to understand and debug.
State is mutable data managed within a component. When the state changes, React automatically re-renders the component to reflect the new data.
This unidirectional data flow is a core principle of React and helps maintain predictable application behavior.
Hooks were introduced in React 16.8 and revolutionized the way developers write React components. Some commonly used hooks include:
useState – Manages state in functional components.
useEffect – Handles side effects such as data fetching, subscriptions, and DOM updates.
useContext – Provides a way to share data across components without prop drilling.
useRef – Accesses DOM elements or persists values across renders.
useMemo and useCallback – Optimize performance by memoizing values and functions.
Hooks promote cleaner code, better separation of concerns, and improved reusability through custom hooks.
Since React focuses only on the UI layer, routing is handled using third-party libraries. The most popular routing solution is React Router. It enables developers to create single-page applications with multiple views and URLs.
React Router allows navigation without reloading the page, improving performance and user experience. It also supports features such as dynamic routing, nested routes, and route-based code splitting.
For small applications, React’s built-in state management using useState and useContext is sufficient. However, as applications grow, managing shared state across many components can become challenging.
To address this, developers often use external state management libraries such as:
Redux
Zustand
MobX
Recoil
Redux, in particular, has been widely adopted for managing complex application state in a predictable and centralized manner. Modern Redux with Redux Toolkit has simplified configuration and reduced boilerplate code.
React has a rich ecosystem of tools and libraries that enhance development productivity. Some popular tools include:
Create React App (CRA) – A quick way to set up a React project.
Vite – A fast build tool and development server.
Next.js – A React framework for server-side rendering and static site generation.
ESLint and Prettier – Tools for code quality and formatting.
Jest and React Testing Library – Testing tools for React applications.
This ecosystem allows developers to build everything from small projects to large enterprise-level applications.
React is widely used by companies such as Facebook, Instagram, Netflix, Airbnb, and Uber. Its popularity is driven by its flexibility, strong community support, and continuous improvements. React’s ability to integrate with other libraries and frameworks makes it suitable for a wide range of use cases, including web apps, mobile apps (using React Native), and even desktop applications.
Some key advantages of React include:
Reusable components that reduce code duplication
High performance due to the Virtual DOM
Strong community and extensive ecosystem
Easy integration with other libraries
Declarative and predictable UI updates
Answer:
React is an open-source JavaScript library developed by Facebook (Meta) for building user interfaces, especially single-page applications (SPAs). It allows developers to create reusable UI components and efficiently update the UI when data changes.
Key points:
Component-based architecture
Uses Virtual DOM
Declarative UI
Fast and scalable
Answer:
React provides several powerful features:
Component-Based Architecture – UI is divided into reusable components
Virtual DOM – Improves performance
JSX – HTML-like syntax inside JavaScript
Unidirectional Data Flow – Data flows from parent to child
Hooks – Add state and lifecycle features to functional components
Answer:
JSX stands for JavaScript XML. It allows writing HTML-like code inside JavaScript.
Example:
const element = <h1>Hello React</h1>;
Why JSX?
Makes code readable
Easy to write UI logic
Prevents injection attacks
Answer:
The Virtual DOM is a lightweight copy of the real DOM. React updates the Virtual DOM first and then compares it with the previous version using a process called diffing.
Only the changed parts are updated in the real DOM, improving performance.
Answer:
A component is a reusable piece of UI. Each component can have its own logic and design.
Types of components:
Functional Components (Recommended)
Class Components (Older approach)
Answer:
A functional component is a JavaScript function that returns JSX.
Example:
function Welcome() {
return <h1>Welcome to React</h1>;
}
Answer:
Props (properties) are read-only inputs passed from a parent component to a child component.
Example:
function Greeting(props) {
return <h2>Hello {props.name}</h2>;
}
Key points:
Props are immutable
Used for component communication
Answer:
State is a built-in object used to store data that can change over time.
Example using useState hook:
const [count, setCount] = React.useState(0);
Difference between Props and State:
| Props | State |
|---|---|
| Passed from parent | Managed within component |
| Read-only | Can be updated |
| Immutable | Mutable |
Answer:
Hooks allow functional components to use state and lifecycle features.
Common Hooks:
useState
useEffect
useContext
useRef
useMemo
Answer:
useEffect is used to perform side effects like API calls, subscriptions, or DOM updates.
Example:
useEffect(() => {
console.log("Component mounted");
}, []);
Answer:
componentDidMount is used in class components
useEffect is used in functional components
useEffect can handle mount, update, and unmount logic.
Answer:
Conditional rendering means displaying components based on a condition.
Example:
{isLoggedIn ? <Dashboard /> : <Login />}
Answer:
React handles events using camelCase syntax.
Example:
<button onClick={handleClick}>Click Me</button>
Answer:
A controlled component is a form element whose value is controlled by React state.
Example:
<input value={name} onChange={e => setName(e.target.value)} />
Answer:
When multiple components need the same data, the state is moved to their common parent component.
Answer:
Keys help React identify which items have changed in a list.
Example:
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
Answer:
React Router is used for navigation in React applications.
Example:
<Route path="/about" element={<About />} />
Answer:
Context API allows sharing data globally without passing props manually.
Used for:
Theme
Authentication
Language settings
Answer:
Redux is a state management library used for managing global state in large applications.
Answer:
Fragments allow grouping elements without adding extra nodes to the DOM.
Example:
<>
<h1>Title</h1>
<p>Description</p>
</>
Answer:
Strict Mode helps identify potential problems during development.
<React.StrictMode>
<App />
</React.StrictMode>
Answer:
Prop drilling occurs when props are passed through many levels unnecessarily.
Solution: Context API or Redux.
Answer:
Memoization improves performance by preventing unnecessary re-renders using:
React.memo
useMemo
useCallback
Answer:
| React | Angular |
|---|---|
| Library | Framework |
| JSX | TypeScript |
| Flexible | Opinionated |
Answer:
Fast rendering
Reusable components
Strong community
Backed by Meta
Easy to learn
Answer:
| Real DOM | Virtual DOM |
|---|---|
| Updates entire UI | Updates only changed parts |
| Slower | Faster |
| Direct manipulation | Lightweight JS object |
| High memory usage | Efficient memory usage |
Explanation:
React updates the Virtual DOM first, compares it with the previous version, and then updates only the changed nodes in the Real DOM.
Answer:
Reconciliation is the process React uses to compare the new Virtual DOM with the old one to determine what needs to change in the real DOM.
It uses:
Diffing algorithm
Keys to identify elements
Answer:
A Pure Component prevents unnecessary re-renders by doing a shallow comparison of props and state.
In functional components:
export default React.memo(MyComponent);
Answer:
React.memo is a higher-order component that memorizes a component and re-renders it only when props change.
Used for performance optimization.
Answer:
useCallback returns a memoized version of a function to prevent re-creation on every render.
Example:
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]);
Answer:
useMemo memorizes the result of a computation.
Example:
const total = useMemo(() => calculateTotal(items), [items]);
Difference:
useCallback → memorizes functions
useMemo → memorizes values
Answer:
useRef is used to:
Access DOM elements
Store mutable values without re-render
Example:
const inputRef = useRef();
inputRef.current.focus();
Answer:
forwardRef allows passing refs from parent to child components.
const Input = React.forwardRef((props, ref) => (
<input ref={ref} />
));
Answer:
A HOC is a function that takes a component and returns an enhanced component.
Example:
const withAuth = (Component) => (props) => {
return <Component {...props} />;
};
Answer:
Render props is a technique where a component shares logic using a function as a prop.
Answer:
Lazy loading loads components only when needed, improving performance.
const About = React.lazy(() => import('./About'));
Used with Suspense.
Answer:
Suspense shows a fallback UI while loading components.
<Suspense fallback={<Loading />}>
<About />
</Suspense>
Answer:
Error boundaries catch JavaScript errors in components and display fallback UI.
Implemented only using class components.
Answer:
| Controlled | Uncontrolled |
|---|---|
| Managed by state | Managed by DOM |
| More control | Less code |
| Recommended | Not preferred |
Answer:
PropTypes are used for type checking of props.
Component.propTypes = {
name: PropTypes.string
};
Answer:
Used to assign default values to props.
Answer:
React Fiber is the new reconciliation engine introduced in React 16 that improves performance and responsiveness.
Answer:
Used to consume context values without wrapping components in Consumer.
const theme = useContext(ThemeContext);
Answer:
State should never be modified directly.
β Wrong:
state.count++;
β Correct:
setCount(count + 1);
Answer:
React groups multiple state updates into a single re-render to improve performance.
Answer:
React wraps browser events into a Synthetic Event for cross-browser compatibility.
Answer:
Portals allow rendering components outside the parent DOM hierarchy.
Used for:
Modals
Tooltips
Answer:
SSR renders React components on the server for:
Faster load time
Better SEO
Used in Next.js.
Answer:
Hydration attaches React event handlers to server-rendered HTML.
Answer:
Mutating state directly
Missing keys in lists
Overusing useEffect
Not understanding props vs state
| Class Component | Functional Component |
|---|---|
Uses extends React.Component |
JavaScript function |
Uses render() method |
Returns JSX directly |
| Can have state and lifecycle methods | Before hooks, no state or lifecycle; now can with hooks |
| More boilerplate | Less boilerplate |
this keyword is used |
No this keyword |
Example Class Component:
class Welcome extends React.Component {
render() {
return <h1>Hello {this.props.name}</h1>;
}
}
Example Functional Component:
function Welcome(props) {
return <h1>Hello {props.name}</h1>;
}
Lifecycle methods are available only in class components. They allow you to run code at specific stages of a component’s life.
Phases:
Mounting: Component is created and added to DOM
constructor()
static getDerivedStateFromProps()
render()
componentDidMount()
Updating: Component updates due to state/props change
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
Unmounting: Component removed from DOM
componentWillUnmount()
Functional components now use useEffect to mimic lifecycle behavior.
| State | Context |
|---|---|
| Managed at component level | Managed globally |
| Triggers re-render in the component | Can provide data to multiple components without props drilling |
| Local updates | Shared updates |
Prop Drilling: Passing props through multiple components unnecessarily.
Example:
<App>
<Parent prop="value">
<Child />
</Parent>
</App>
Solution:
Context API
Redux / Zustand
React Router: A library to handle routing in SPAs.
Features:
Declarative routing
Nested routes
Dynamic routes
Navigation without page reload
Example:
import { BrowserRouter, Routes, Route } from "react-router-dom";
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</BrowserRouter>
// Empty dependency array
useEffect(() => {
console.log("Runs only once");
}, []);
// No dependency array
useEffect(() => {
console.log("Runs on every render");
});
[] → Mount only
No array → Every render
Controlled Input: Value controlled by React state
<input value={name} onChange={(e) => setName(e.target.value)} />
Uncontrolled Input: Value handled by DOM, using ref
<input ref={inputRef} />
| useState | useReducer |
|---|---|
| Simple state logic | Complex state logic |
| Single variable or object | Multiple state transitions |
| Easy to use | More structured |
Example: const [count, setCount] = useState(0) |
Example: const [state, dispatch] = useReducer(reducer, initialState) |
| React | React Native |
|---|---|
| For web apps | For mobile apps |
| Uses HTML and CSS | Uses native components (View, Text) |
| Browser DOM | Native UI components |
Example: <div> |
Example: <View> |
| useEffect | useLayoutEffect |
|---|---|
| Runs after rendering | Runs synchronously before painting |
| Doesn’t block DOM | Blocks DOM updates |
| Good for API calls, subscriptions | Good for measuring DOM |
StrictMode is a wrapper component that helps detect potential problems in development:
<React.StrictMode>
<App />
</React.StrictMode>
Checks for unsafe lifecycle methods
Identifies legacy API usage
Double-invokes certain functions in development
| useRef | createRef |
|---|---|
| Functional components | Class components |
| Persistent between renders | New ref created on each render |
const ref = useRef() |
this.ref = React.createRef() |
Component: Re-renders on any state/prop change
PureComponent: Re-renders only if shallow comparison of state/props changes
class MyComponent extends React.PureComponent {}
Pre-renders React components on the server
Sends HTML to the browser
Benefits: SEO, faster first load
Framework: Next.js
Render components outside the parent DOM
Useful for modals, tooltips
ReactDOM.createPortal(<Modal />, document.getElementById('modal-root'))
Function that enhances a component
Example: Authentication HOC
const withAuth = (Component) => {
return (props) => {
return isLoggedIn ? <Component {...props} /> : <Login />;
};
};
Components loaded on demand
Improves performance
const About = React.lazy(() => import('./About'));
<Suspense fallback={<Loader />}>
<About />
</Suspense>
Use React.memo for functional components
Use useCallback and useMemo
Avoid unnecessary re-renders
Lazy load components
Split bundle using React.lazy + Suspense
React wraps native events in a SyntheticEvent
Events are pooled to improve performance
Use event.persist() to access event asynchronously
When using SSR, React attaches event listeners to server-rendered HTML
Optimizes client-side rendering
| Class Components | Functional Components (Hooks) |
|---|---|
componentDidMount → useEffect(() => {}, []) |
useEffect mimics mount, update, unmount |
componentDidUpdate → useEffect(() => {}, [deps]) |
Dependence array controls update behavior |
componentWillUnmount → return () => {} inside useEffect |
Cleanup function in useEffect handles unmount |
State using this.setState |
useState hook |
| More boilerplate | Less boilerplate, cleaner |
Example: Lifecycle with Hooks
useEffect(() => {
console.log("Component mounted");
return () => console.log("Component unmounted");
}, []);
React compares the new Virtual DOM tree with the old one (diffing).
Only the changed nodes are updated in the real DOM.
Keys in lists help React optimize this comparison.
Tip for interviews: Explain O(n) complexity and the importance of keys in dynamic lists.
Techniques:
Use React.memo for functional components
Use PureComponent for class components
Use useCallback for functions passed as props
Use useMemo for expensive calculations
Avoid changing state unnecessarily
Example:
const MemoizedChild = React.memo(Child);
Reconciliation is React’s process of updating the DOM efficiently.
React checks which elements changed, were added, or removed.
Uses Virtual DOM + keys for optimal updates.
Key point: Always assign unique keys in lists.
| Context API | Redux |
|---|---|
| Built-in React | External library |
| Ideal for small apps | Ideal for large apps |
| Less boilerplate | Centralized state management |
| No middleware support | Middleware: Thunk, Saga |
Scenario:
Theme toggling → Context
Shopping cart state → Redux
Use useEffect for fetching data
Use async/await
Handle loading and error states
Cancel subscriptions on unmount using AbortController
Example:
useEffect(() => {
const controller = new AbortController();
fetch("https://api.example.com", { signal: controller.signal })
.then(res => res.json())
.then(setData)
.catch(err => console.error(err));
return () => controller.abort();
}, []);
useEffect dependency array work?Empty array ([]) → runs once (componentDidMount)
No array → runs after every render
Array with dependencies → runs when dependencies change
Example:
useEffect(() => {
console.log(count);
}, [count]); // runs only when count changes
useLayoutEffect and useEffect| useEffect | useLayoutEffect |
|---|---|
| Runs after painting | Runs before painting |
| Non-blocking | Blocking |
| API calls, subscriptions | DOM measurements, layout calculations |
Scenario: Measuring a div width before render → useLayoutEffect
Use React.lazy + Suspense
Load components on demand to reduce initial bundle size
const About = React.lazy(() => import('./About'));
<Suspense fallback={<Loader />}>
<About />
</Suspense>
Also possible with dynamic import in routes (React Router).
A HOC is a function that takes a component and returns a new component
Used for:
Authentication (withAuth)
Logging
Theme wrapping
Example:
const withLogger = (Component) => (props) => {
console.log("Props:", props);
return <Component {...props} />;
};
Controlled Components:
Value controlled by React state
<input value={name} onChange={(e) => setName(e.target.value)} />
Uncontrolled Components:
Value controlled by DOM
<input ref={inputRef} />
Use case: Form validation → Controlled Component
Virtualization → react-window or react-virtualized
Keys for stable identity
Memoized list items
Pagination or lazy loading
Prop Drilling: Passing props through multiple levels unnecessarily.
Solution:
Context API
Redux / Zustand / Recoil
<ThemeContext.Provider value={theme}>
<Component />
</ThemeContext.Provider>
Controlled inputs → React state
Uncontrolled inputs → refs
Use libraries: Formik, React Hook Form
Example (React Hook Form):
const { register, handleSubmit } = useForm();
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("name")} />
</form>
useReducer and when to use ituseReducer is ideal for complex state logic
Alternative to useState when multiple state variables depend on each other
const initialState = { count: 0 };
function reducer(state, action) {
switch(action.type) {
case 'increment': return { count: state.count + 1 };
default: return state;
}
}
const [state, dispatch] = useReducer(reducer, initialState);
Options:
Redux → For large apps with multiple modules
Context API → For small apps
Zustand / Recoil → Lightweight alternative
React Query / SWR → For server state
SSR: React components rendered on server, sent as HTML
Hydration: React attaches event listeners to server-rendered HTML
Framework: Next.js
React DevTools → Component tree, props, state
Console.log → Quick debugging
Profiler → Performance bottlenecks
Error Boundaries → Catch errors gracefully
Only class components can be error boundaries
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError() {
return { hasError: true };
}
render() {
if (this.state.hasError) return <h1>Something went wrong</h1>;
return this.props.children;
}
}
React.memo for functional components
useCallback / useMemo
Avoid anonymous functions in render
Split components to minimize re-rendering
Lazy load components
Virtualize large lists
| Hook | When it runs | Use case |
|---|---|---|
useEffect |
After the render is painted | Fetching API data, subscriptions |
useLayoutEffect |
Before painting the DOM | Measuring layout, reading DOM dimensions |
useInsertionEffect |
Before any styles are injected | Injecting CSS in libraries like styled-components |
Example:
useLayoutEffect(() => {
const width = divRef.current.offsetWidth;
}, []);
Render props is a pattern where a component receives a function as a prop to render dynamic content.
Used to share logic between components without HOC.
Example:
<DataFetcher render={(data) => <List items={data} />} />
React does not attach events directly to DOM nodes.
All events are handled via SyntheticEvent at the root (document level).
Ensures cross-browser compatibility and better performance.
With keys: React efficiently reuses elements
Without keys: React re-renders all elements unnecessarily
Example:
{items.map(item => <li key={item.id}>{item.name}</li>)}
React Fiber (React 16+) is the new reconciliation engine
Allows:
Incremental rendering
Interruptible rendering
Prioritizing updates (high vs low priority)
Improves responsiveness for complex apps
Error boundaries cannot be functional directly
Use libraries like react-error-boundary
<ErrorBoundary FallbackComponent={ErrorFallback}>
<MyComponent />
</ErrorBoundary>
Controlled forms:
React state handles inputs
Easier validation
Better for dynamic forms
Uncontrolled forms:
Use ref for simple forms
Less boilerplate
Example (Controlled):
<input value={form.name} onChange={handleChange} />
Debouncing inputs to avoid multiple calls
Caching using SWR or React Query
Batching requests to reduce network calls
Canceling requests on unmount with AbortController
React.memo and its caveatsReact.memo prevents re-rendering if props are the same
Caveats:
Works only for functional components
Shallow comparison only
Won’t prevent re-render if object/array props change reference
const MemoChild = React.memo(Child);
useCallback vs useMemo| Hook | Purpose | Example |
|---|---|---|
useCallback |
Memoize function references | const memoFn = useCallback(fn, [deps]) |
useMemo |
Memoize value/result | const memoValue = useMemo(() => computeExpensive(), [deps]) |
Scenario: Passing functions to child components to avoid unnecessary re-render.
Allows interruptible rendering, improves app responsiveness
Enables:
Suspense for data fetching
Rendering without blocking UI
Still experimental in React 18+
const About = React.lazy(() => import("./About"));
<Routes>
<Route path="/about" element={
<Suspense fallback={<Loader />}>
<About />
</Suspense>
}/>
</Routes>
Reduces initial bundle size
Improves performance
SSR (Server-Side Rendering): Server sends pre-rendered HTML
Hydration: React attaches event listeners to server HTML
Frameworks: Next.js, Remix
Benefit: SEO + faster first paint
Options:
Redux → Centralized state + middleware (Thunk/Saga)
Context API → For small-medium apps
Zustand / Recoil → Lightweight alternatives
React Query / SWR → For server state caching
Tip: Combine Redux + React Query for large apps.
Virtualization → react-window, react-virtualized
Memoization → React.memo for list items
Pagination / Infinite Scroll
Keys for list items
Unit tests: Jest + React Testing Library
Integration tests: For components working together
E2E tests: Cypress or Playwright
Example (React Testing Library):
render(<Button label="Click me" />);
expect(screen.getByText(/click me/i)).toBeInTheDocument();
JWT / OAuth + API backend
Store token: localStorage or httpOnly cookie
Use Context API / Redux to store user state
Use PrivateRoute component to protect routes
<Route path="/dashboard" element={user ? <Dashboard /> : <Login />} />
Avoid unnecessary dependencies
Avoid side effects that trigger re-render
Use cleanup functions
Memoize heavy computations
Use Context API for global theme
Store user preference in localStorage
Example:
<ThemeContext.Provider value={theme}>
<App />
</ThemeContext.Provider>
Error boundaries
External logging services: Sentry, LogRocket
Global API error handling
Monitoring unhandled promise rejections
| Feature | CSR | SSR |
|---|---|---|
| Render location | Browser | Server |
| Initial load | Slower (JS downloads first) | Faster (HTML already rendered) |
| SEO | Limited | SEO-friendly |
| Hydration | Not required | Required (attach event listeners) |
| Framework | Create React App | Next.js, Remix |
Tip: Use SSR for SEO-heavy apps, CSR for dashboard apps.
Suspense allows pausing the rendering of a component until some condition is met (e.g., data loaded, lazy component imported).
Used with React.lazy and data fetching libraries.
<Suspense fallback={<Loader />}>
<LazyComponent />
</Suspense>
Use case: Lazy loading routes or images.
| React.lazy | Dynamic Import |
|---|---|
| Only for components | Can be used for any module |
| Works with Suspense | Can work standalone |
| Clean syntax for lazy components | More flexible |
Portals allow rendering a child component outside the parent DOM hierarchy.
Useful for:
Modals
Tooltips
Dropdowns
ReactDOM.createPortal(<Modal />, document.getElementById('modal-root'));
React wraps native events in SyntheticEvent
Provides cross-browser compatibility
Pooling mechanism improves performance
Example:
function handleClick(event) {
console.log(event.type); // SyntheticEvent
}
Use event.persist() to retain the event for async calls.
Use useEffect for:
Fetching APIs
Subscriptions
Timers
DOM manipulations
Example:
useEffect(() => {
const timer = setInterval(() => console.log("tick"), 1000);
return () => clearInterval(timer); // cleanup
}, []);
| Feature | useState | useReducer |
|---|---|---|
| State complexity | Simple | Complex/multi-value |
| Updates | Direct (setState) |
Action + reducer |
| Best for | Small forms or single value | Large forms, multi-step, or nested state |
Example (useReducer):
const [state, dispatch] = useReducer(reducer, {count: 0});
dispatch({type: 'increment'});
React.memo → Prevent re-rendering functional components
useCallback → Memoize functions
useMemo → Memoize expensive calculations
Example:
const memoizedValue = useMemo(() => computeExpensive(items), [items]);
Allows interruptible rendering for responsiveness
Prioritizes important updates over less important ones
Enables Suspense for data fetching
Still experimental
Use case: Rendering a large list without blocking UI.
Use controlled components with minimal re-renders
useReducer to manage form state
Use React Hook Form for performance
Debounce validation
Lazy load optional form sections
Store JWT tokens securely (httpOnly cookies preferred)
Protect routes using PrivateRoute
Store user state in Context API or Redux
<Route path="/dashboard" element={user ? <Dashboard /> : <Login />} />
Wrap API calls in try-catch
Show loading/error states in UI
Cancel API requests on unmount using AbortController
Centralized error handling in Redux middleware or React Query
| Feature | useRef | createRef |
|---|---|---|
| Functional component | β | β |
| Class component | β | β |
| Persist value between renders | β | β (new ref on each render) |
Example:
const inputRef = useRef();
<input ref={inputRef} />
Small apps: Context API
Medium apps: Context + useReducer
Large apps: Redux / Zustand / Recoil
Server state: React Query / SWR
Example (Redux):
const store = configureStore({ reducer: rootReducer });
<Provider store={store}><App /></Provider>
Virtualization: react-window / react-virtualized
Memoization: React.memo for list items
Pagination or infinite scrolling
Keys for list items
| Hook/Method | Purpose | Example |
|---|---|---|
| React.memo | Memoize functional components | const MemoChild = React.memo(Child) |
| useCallback | Memoize function references | const fn = useCallback(() => {}, [deps]) |
| useMemo | Memoize computed values | const val = useMemo(() => expensive(), [deps]) |
Use React.lazy + Suspense for lazy-loading components
Split bundles for routes using dynamic imports
const About = React.lazy(() => import('./About'));
<Suspense fallback={<Loader />}><About /></Suspense>
Unit tests: Jest + React Testing Library
Integration tests: For connected components
E2E tests: Cypress / Playwright
Example:
render(<Button label="Click" />);
expect(screen.getByText(/click/i)).toBeInTheDocument();
Unnecessary re-renders (missing memoization)
Updating state incorrectly (mutating objects/arrays)
Large lists without virtualization
Heavy computations inside render
Overusing useEffect without dependency arrays
CSS Modules → Scoped styles
Styled Components / Emotion → CSS-in-JS
TailwindCSS → Utility-first approach
SASS / SCSS → Preprocessor
Inline styles for dynamic properties