79 React Interview Questions for Beginners
Prepare for your next React interview with this list of 70 questions, perfect for beginners and updated to 2024. Whether you're brushing up on the basics or learning React for the first time, these questions will help you ace your technical interviews with confidence.

Get the latest frontend development resources delivered straight to your inbox for free.
Explore our latest issue for a sample.
🚀 The Complete Web Developer in 2024
- Efficiently updates and renders components when data changes.
- Helps manage UI complexity in large applications.
- Platform-agnostic (works for web, mobile, 3D apps).
- Different parts of Facebook’s UI got out of sync because they were powered by separate views.
- React ensures that when the state changes, the UI updates automatically, fixing the issue.
- Function components (preferred): Simple JavaScript functions.
- Class components: Legacy, based on ES6 classes.
- React uses a virtual DOM to manage updates efficiently.
- It interacts with the actual DOM to create, update, and delete elements based on the application state.
- Reduces the number of costly DOM manipulations.
- Improves performance by applying only necessary changes to the real DOM.
- The HTML element type (
'p'
for paragraph). - Properties (an object containing attributes, like
{ id: 'hello' }
). - The content/children of the element (
'Hello World!'
). - The
React.createElement
function returns a JavaScript object that describes the element. - This object contains information like the type of the element, its props (properties), and its children.
- React uses this object to eventually render the actual DOM element.
- Props are read-only.
- Data flows one way (parent → child).
- Props help make components reusable.
- Select the root element in the DOM.
- Create the root with
ReactDOM.createRoot
. - Render the React element using
root.render()
. - React: Handles the logic and structure of components (e.g.,
React.createElement
, state, props). - ReactDOM: Bridges React and the browser DOM (e.g.,
ReactDOM.render
orReactDOM.createRoot
). - React 18's createRoot() can handle updates in the background, making apps faster than React 17’s ReactDOM.render(), which did everything at once.
- In React 18, updates (even for things like timeouts) are grouped together, so the app refreshes less often. React 17 only did this for clicks or other direct user actions.
- React 18's Suspense feature now helps load data more smoothly, making the app feel faster.
- createRoot() gives developers more control over how the app updates and allows smoother, faster changes.
- Handles multiple JS files and dependencies.
- Improves loading time and application performance.
- Allows code to use advanced JS features like JSX.
- Include React and ReactDOM scripts using a CDN.
- Use
React.createElement
to define components. - Use
ReactDOM.createRoot
to render the component. react-dom
for web applications.react-native
for mobile apps (iOS/Android).react-three-fiber
for 3D applications using WebGL.- Automatically handles React imports
- Uses an optimized _jsx function
- Removes the need to import React manually
- Produces more efficient code
- Tag Closing: All elements must be explicitly closed, unlike HTML where certain tags, such as
<br>
or<img>
, can be left unclosed. - Attributes: JSX attributes use camelCase naming conventions because JSX integrates closely with JavaScript, which is case-sensitive. This ensures consistency between JSX and JavaScript properties.
- Reserved Words: Certain words, like class and for, are reserved in JavaScript because they have predefined meanings. To avoid conflicts, these words must be replaced in JSX with className and htmlFor, respectively.
- Lowercase tags for standard HTML elements (
div
,p
,span
). - Uppercase tags for custom React components (e.g.,
MyComponent
). - The JSX compiler uses tag names to decide whether to render a native DOM element or invoke a custom component.
- Writing tags in the wrong case can lead to errors or unexpected behavior.
- The double curly braces represent: outer pair for JSX expression, inner pair for object literal
- Property names are in camelCase instead of kebab-case
- Values must be strings or numbers
- Numbers automatically get 'px' appended for relevant properties
- Vendor prefixes start with a capital letter (e.g., WebkitTransition)
- JSX automatically translates these attributes into valid HTML without modification.
- They are particularly useful when enhancing user experience or ensuring compliance with accessibility standards.
useState
for state managementuseEffect
for managing side effects (like API calls)useRef
for accessing DOM elements directlystate
holds the current state value.setState
updates the state value.initialValue
is the starting value for the state.- Always call
useState
at the top level of a functional component. - Hooks cannot be used conditionally.
- Changing state triggers a re-render of the component.
- Its state or props change.
- A parent component re-renders it.
- Form elements (like
<input>
or<select>
) derive their values from React state. - Updates happen via an onChange event handler.
- Never use hooks inside loops, conditionals, or nested functions.
- Always call hooks at the top level of your component.
- Screen readers work better with native form elements.
- Using
<div>
for input logic is discouraged unless absolutely necessary. - Fetching data from an API.
- Updating the browser DOM manually.
- Setting up timers, subscriptions, or event listeners.
- Runs after the render and can optionally clean up after itself.
- Accepts a dependency array as the second argument to control when it runs:
- No array: Runs after every render.
- Empty array ([]): Runs only once after the first render.
- Dependencies: Runs whenever specified dependencies change.
- Return a function from the useEffect callback for cleanup.
- Useful for canceling requests or clearing intervals.
useEffect
without a dependency array runs on every render.- State updates inside
useEffect
can cause re-renders, creating a loop if dependencies aren't properly managed. - Add dependencies to the array to prevent unnecessary calls.
- Initialize
loading
totrue
before the fetch starts. - Set loading to false after the data is fetched.
- Use conditional rendering (e.g., ternary operators) to display "Loading..." until data is available.
- Include all state or props accessed in the effect in the dependency array.
- Avoid leaving the array empty unless the effect should only run once.
- Avoid direct updates to dependencies within the effect to prevent loops.
- Keeps the DOM stable during re-renders.
- Helps React differentiate between elements, especially in lists.
- Prevents potential rendering bugs when elements are swapped or reordered.
- Assign a unique value (like id) as the key.
- Avoid using indices as keys unless absolutely necessary.
- Does not uniquely identify items when their order changes.
- React may mistakenly associate incorrect elements with their previous position.
- Static lists: The list does not change (no additions, deletions, or reordering).
- Non-critical elements: Rendering inaccuracies won't impact the app's functionality or user experience.
- Performance considerations: Simpler cases where the overhead of assigning unique keys is unnecessary.
- Detects unsafe lifecycle methods.
- Warns about deprecated APIs.
- Double invokes functions like useEffect to catch subtle bugs.
- A custom hook is just a JavaScript function that uses built-in React hooks (e.g.,
useState
,useEffect
). - It follows the naming convention of starting with use (e.g., usePizzaOfTheDay).
- It helps keep components clean and focused by moving logic into reusable functions.
- Reusability: Extract repetitive logic into a single function.
- Composability: Combine multiple hooks for more complex functionality.
- Readability: Keeps components clean and easier to maintain.
- Prefix with use: Helps React identify it as a hook.
- Follow rules of hooks: Only call hooks at the top level of the function, not inside loops or conditions.
- Encapsulate logic: Focus on a single purpose or responsibility.
- Return useful values or functions: Ensure the return values provide what the caller needs (e.g., state, functions).
- When you need to share stateful logic across components.
- When the logic is complex and needs to be tested separately.
- When you want to separate concerns, keeping components focused on rendering UI.
- Primarily used for custom hooks
- Provides additional visibility in React DevTools
- Only useful during development, does not impact production code
- Can display complex or simplified representations of hook state
- Handles both button clicks and pressing Enter.
- Improves accessibility without extra effort.
- Integrates naturally with form validation.
- State is managed within the component.
- Changes to state trigger re-renders.
- Use useState for managing state in functional components.
- The child doesn’t directly modify the parent’s state.
- The parent provides a callback function to handle changes.
- Use async/await for clarity.
- Update state to reflect the operation status (e.g., loading, error).
- Handle errors with try/catch.
- When the data is required in multiple components spread across different levels.
- When managing app-level state like:
- User Information (e.g., name, preferences, etc.)
- Theme (dark/light mode)
- Cart or persistent data that should not reset between page loads.
- The state is localized or can be easily passed via props.
- It's only solving the prop-drilling problem in a single section. In such cases, props may be a better option to keep the data flow explicit.
- Tight Coupling: Components become dependent on the shared state.
- Testing Complexity: Mocking context for tests adds overhead.
- Performance: Frequent updates to context can trigger unnecessary re-renders across components consuming it.
- Simplifies context consumption in functional components.
- Avoids the need for prop drilling by sharing state globally across the app.
- Debugging Challenges: It's not always clear where the context is modified, especially in large applications.
- Performance Concerns: Unnecessary re-renders can occur if the context value changes frequently.
- Code Readability: The global nature of contexts can make it hard to track the flow of data and pinpoint issues.
- Use descriptive context names (e.g., CartContext) to improve clarity.
- Only place essential shared state in a context to avoid overusing it.
- Combine context with other state management techniques (e.g., reducers or libraries like Zustand) for complex applications.
- Routes are managed on the client side.
- No full-page reloads; content changes dynamically.
- Examples: Gmail, Google Maps.
- Improves performance for large applications.
- Avoids loading unnecessary code upfront.
- Useful for heavy resources like Three.js.
- Enables faster navigation.
- Manages browser history seamlessly.
- Optimized for single-page applications (SPAs).
- You might need a modal, tooltip, or side navigation that visually appears “above” or “outside” your main app structure.
- Portals help avoid awkward workarounds with CSS z-index or complicated component nesting.
- Better DOM organization: keeps modal or side content out of the main DOM hierarchy.
- Easier styling: no fighting with parent elements’ CSS.
- More flexibility: you can place interactive elements above everything else without messing up parent component layouts.
- Defined using ES6 classes (e.g.,
class MyComponent extends React.Component
). - Use
this.setState
to update state. - Have lifecycle methods (e.g.,
componentDidMount
,componentDidUpdate
) instead of hooks. - Must include a
render()
method that returns JSX. - Catches errors in child components only (not itself).
- Prevents the whole app from unmounting when an error occurs.
- Implements specific lifecycle methods: static getDerivedStateFromError and componentDidCatch.
- Typically used to show a “Something went wrong” message or to log errors to a service.
- Prevent the “white screen of death” in production.
- Display a user-friendly message when errors happen.
- Easy to log errors to tracking services (e.g., Sentry, TrackJS).
- Hooks were introduced for function components.
- Class components have their own APIs:
componentDidMount
,componentDidUpdate
, etc. - If needed, you can wrap your class component with a small function component that uses hooks and then pass the values via props.
componentDidMount
(class) ≈useEffect(() => { ... }, [])
(function).componentDidUpdate
(class) ≈useEffect(() => { ... })
(function).componentWillUnmount
(class) ≈ the cleanup function returned byuseEffect
.- Must be static, so it’s called on the class itself.
- Used specifically within error boundaries to set
hasError
. - Paired with
componentDidCatch
for full error-handling. - Create a function component that calls the hook.
- Pass the result as props to the class component. This approach is sometimes called a “bridge” component.
What is React, and why was it developed?
React is a JavaScript library for building dynamic user interfaces. It was developed by Facebook to solve issues with UI consistency and state synchronization across different parts of the app.
Key features of React:
What problem did React solve for Facebook?
React helped solve the issue of "phantom messages" by keeping the UI and state synchronized, eliminating bugs caused by inconsistencies between different parts of the UI.
Why was this important?
What is a React component?
A React component is a reusable piece of UI. It is either a function or a class that accepts inputs (props) and returns React elements to describe what should appear on the screen.
Types of components:
Example (Function Component):
const Greeting = () => {
return React.createElement('h1', null, 'Hello, React Component!');
};
Example (Class Component):
class Greeting extends React.Component {
render() {
return React.createElement('h1', null, 'Hello from Class Component!');
}
}
What is the DOM, and how does React interact with it?
The DOM (Document Object Model) is a tree-like structure that represents a web page's HTML elements. It allows programming languages like JavaScript to dynamically interact with the content, structure, and style of a website.
Example of a DOM Structure:
For this HTML:
<!DOCTYPE html>
<html>
<body>
<div id="root">
<h1>Hello World</h1>
<p>This is a paragraph.</p>
</div>
</body>
</html>
The DOM tree structure would look like this:
html
├── body
└── div#root
├── h1 (Hello World)
└── p (This is a paragraph.)
Key points:
How does React’s virtual DOM differ from the regular DOM?
The virtual DOM is a lightweight copy of the real DOM. React uses it to determine the minimal changes necessary to update the actual DOM.
Advantages:
How do you create a basic React element?
You can create a React element using the React.createElement
function.
const element = React.createElement('p', { id: 'hello' }, 'Hello World!')
Arguments:
How React.createElement
works:
{
type: 'p',
key: null,
ref: null,
props: {
id: 'hello',
children: 'Hello World!',
},
_owner: null,
_store: {}
}
What are props in React, and how do you use them?
Props (short for properties) are inputs to a React component that are passed down from a parent component. They are immutable and used to pass data or functions to child components.
Key Points:
const Pizza = (props) => {
return (
<div>
<h1>{props.name}</h1>
<p>{props.description}</p>
</div>
);
};
const App = () => {
return <Pizza name="Hawaiian Pizza" description="Pineapple and ham" />;
};
How do you render a React element to the DOM?
React elements are rendered to the DOM using the render
method.
import { createRoot } from 'react-dom/client'
const container = document.querySelector('#root')
const root = createRoot(container)
root.render(element)
Steps:
How do React and ReactDOM differ?
React is the core library for building user interfaces, while ReactDOM is the library for rendering React components into the DOM.
// Import the libraries
import React from 'react';
import ReactDOM from 'react-dom/client';
// Create a React component
const App = () => React.createElement('h1', null, 'Hello from React!');
// Render the component into the DOM
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(App());
What are the differences between React 17 and React 18 rendering methods?
In React 17, the ReactDOM.render
method was used to render components, while in React 18, this was changed to createRoot
and render
.
Old approach:
ReactDOM.render(element, container)
New approach (React 18):
const root = createRoot(container)
root.render(element)
Key Reasons for React 17 to React 18 Update:
How does the build system help React applications?
React uses a build system to bundle all JavaScript files and dependencies together into one output, optimizing performance and making modern JavaScript features available.
Benefits:
How can you serve a React application without a build step?
You can serve a React application without a build step by using a CDN to load React and ReactDOM, creating a basic HTML file, and writing JavaScript directly.
Steps:
<!DOCTYPE html>
<html lang="en">
<head>
<title>React Without Build</title>
</head>
<body>
<div id="root"></div>
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<script>
const root = ReactDOM.createRoot(document.getElementById('root'));
const App = () => React.createElement('h1', null, 'Hello React Without Build!');
root.render(React.createElement(App));
</script>
</body>
</html>
What platforms can React be used for?
React is platform-agnostic, meaning it can be used to build UIs for web, mobile, and even 3D environments.
Common platforms React supports:
What is the role of createRoot in React 18?
In React 18, createRoot is a key API for managing how a React application is rendered and how its lifecycle is handled. It is part of the transition to a more efficient rendering system that introduces concurrent rendering, improving the responsiveness of React applications.
const container = document.querySelector('#root')
const root = createRoot(container)
root.render(<App />)
What is JSX, and why do we need a build system for it?
JSX is a syntax extension for JavaScript that lets you write HTML-like code directly in your JavaScript files. It was popularized by React and makes it much more intuitive to describe UI components.
const element = (
<div className="greeting">
<h1>Hello, {name}</h1>
<p>Welcome to our site!</p>
</div>
);
We need a build system for JSX because:
Browsers don't understand JSX, browsers can only read standard JavaScript. The JSX code above isn't valid JavaScript, so it needs to be transformed into something like:
const element = React.createElement(
"div",
{ className: "greeting" },
React.createElement("h1", null, "Hello, ", name),
React.createElement("p", null, "Welcome to our site!")
);
What is the difference between `.js` and `.jsx` file extensions in React?
Initially, .jsx
was required to differentiate JSX code from plain JavaScript. Now, tools can handle JSX in .js
files, but some build tools like Vite may require .jsx
. It's mainly a convention and depends on project settings.
What's the difference between JSX and traditional React.createElement?
JSX is a simpler way to write React elements. While both create the same result, JSX is much more readable and maintainable.
// JSX way - easier to read
const element = (
<div className="container">
<p>Hello!</p>
</div>
);
// Traditional way - harder to read
const element = React.createElement(
'div',
{ className: 'container' },
React.createElement('p', null, 'Hello!')
);
Why do we use parentheses with JSX
Parentheses in JSX are used for formatting purposes. They allow us to write multi-line JSX code clearly and ensure proper parsing.
const element = (
<div>
<h1>Title</h1>
<p>Content</p>
</div>
);
Do we need to import React to use JSX?
In earlier React versions, importing React was required when using JSX, as JSX compiled into React.createElement
calls. However, starting in React 17, the JSX transformer automatically handles this, so explicit imports aren’t necessary for JSX alone.
The modern JSX transform (React 17+):
// Modern transform result
import { jsx as _jsx } from 'react/jsx-runtime';
const element = _jsx('h1', { children: 'Hello' });
What are Expression Slots in JSX?
Expression slots are created using curly brackets {}
in JSX and allow you to embed JavaScript expressions within JSX code. Anything inside {}
is treated as pure JavaScript instead of a string.
const name = "John";
const element = <div>Hello, {name}!</div>;
How do you add comments in JSX?
Comments in JSX must be placed inside expression slots using the {/* */}
syntax. Single-line comments (//
) don't work because they would break the expression slot closure.
const element = (
<div>
{/* This is a valid JSX comment */}
<p>Some content</p>
</div>
);
How do expression slots work with attributes in JSX?
Attribute expression slots allow you to dynamically set attribute values using JavaScript expressions within curly braces {}.
const uniqueId = 'content-wrapper';
const element = <div id={uniqueId}>Hello World</div>;
Here, the id attribute value is dynamically set to uniqueId.
Equivalent compiled code:
const element = React.createElement(
'div',
{ id: uniqueId },
'Hello World'
);
Can you use expressions other than simple variables in JSX expression slots?
Yes, any valid JavaScript expression can be used within an expression slot, including functions, arithmetic, and string operations.
const userEmail = '[email protected]';
const element = <div id={userEmail.replace('@', '-')}>User Info</div>;
This replaces @ with -, resulting in example-domain.com at runtime.
How does JSX handle type coercion in expression slots for attributes?
JSX automatically converts types as needed in expression slots, especially for boolean and numeric attributes. For example, both a string "true" and a boolean true
can be used interchangeably, as JSX converts these values to meet HTML attribute requirements.
// Both are valid
<input required="true" />
<input required={true} />
// Numeric attributes
<input type="range" min="1" max="5" />
<input type="range" min={1} max={5} />
What's the difference between compile-time and run-time in JSX expressions?
At compile-time, JSX is converted to React.createElement()
calls, but the expressions aren't evaluated. The actual evaluation of expressions happens at run-time when the code executes in the browser. This is why expression slots must contain expressions, not statements.
// This code...
const element = (
<div>
Count: {count + 1}
</div>
);
// ...compiles to:
const element = React.createElement(
'div',
null,
'Count: ',
count + 1
);
What are the limitations and best practices for boolean attributes in JSX?
Boolean attributes in JSX can be written in multiple ways, but the most explicit and recommended approach is to clearly state the value using an expression slot. This practice makes the code more maintainable and future-proof.
Best Practice Example:
// Most explicit and recommended
<input required={true} />
// Less recommended but valid
<input required="true" />
<input required />
What are the differences between HTML and JSX syntax?
JSX is a syntax extension for JavaScript that looks similar to HTML but has some distinct differences. It follows unique rules to integrate seamlessly with React's rendering system:
Why does JSX require self-closing tags for void elements?
In HTML, certain elements, known as "void elements," don’t require a closing tag because they are self-contained and cannot have child elements. Examples of void elements include <img>
, <input>
, <br>
, <meta>
, and <hr>
. Browsers automatically handle these unclosed tags correctly in plain HTML.
While HTML allows flexibility with void elements, JSX requires strict syntax rules to ensure consistency, clarity, and compatibility with JavaScript. This enforcement simplifies debugging, prevents ambiguity, and aligns JSX closely with the logic of the underlying React framework.
Why is case sensitivity important in JSX?
JSX distinguishes between:
This distinction is important because:
// Lowercase for native HTML elements
const element = (
<main>
<header>
<h1>Hello World!</h1>
</header>
<p>Welcome to React.</p>
</main>
);
// Uppercase for custom React components
function WelcomeMessage() {
return <h2>Welcome!</h2>;
}
const app = <WelcomeMessage />;
How do inline styles work in JSX?
Inline styles in JSX differ from traditional HTML styles. Instead of using a string, JSX requires you to pass a JavaScript object to the style attribute.
Important considerations:
// HTML version
<div style="font-size: 16px; background-color: blue; margin-top: 10px;">
// JSX version
<div style={{
fontSize: '16px',
backgroundColor: 'blue',
marginTop: '10px'
}}>
How do data and ARIA attributes differ in JSX?
In JSX, data- and ARIA (aria-) attributes retain their original dashed-case names, unlike standard HTML attributes that often convert to camelCase (e.g., className
instead of class
).
const element = (
<button data-test-id="test-button" aria-label="Close dialog" aria-hidden="false">
Click me
</button>
);
What are React Hooks?
React Hooks are special functions that let you use React features (like state and lifecycle methods) in functional components. They make components interactive and manage side effects or state.
Examples of React Hooks:
import React, { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
What is the useState Hook used for?
The useState
Hook is used to add state to functional components. It lets you declare a state variable and update it when needed.
const [state, setState] = useState(initialValue);
Key Points:
function Example() {
const [name, setName] = useState("John");
return (
<div>
<p>Name: {name}</p>
<button onClick={() => setName("Jane")}>Change Name</button>
</div>
);
}
How does React decide when to re-render a component?
React re-renders a component when:
React uses a Virtual DOM to compare the old and new states of the UI and only updates the changed parts, minimizing real DOM operations.
What is a controlled component in React?
A controlled component is a form element (like an input or select) whose value is controlled by React state. Instead of relying on the browser's default behavior, React fully manages its value through state and updates it when a user interacts with it.
Key Points:
import React, { useState } from "react";
function PizzaOrderForm() {
const [pizzaType, setPizzaType] = useState("Pepperoni");
return (
<div>
<label>
Pizza Type:
<select value={pizzaType} onChange={(e) => setPizzaType(e.target.value)}>
<option value="Pepperoni">Pepperoni</option>
<option value="Hawaiian">Hawaiian</option>
<option value="Veggie">Veggie</option>
</select>
</label>
<p>You selected: {pizzaType}</p>
</div>
);
}
Why does React enforce that hooks are always called in the same order?
React relies on the strict order of hooks in a component to manage internal state. If hooks are called conditionally or out of order, React will mix up the state values, leading to bugs.
Key Points:
Code Example (Incorrect Usage):
if (someCondition) {
const [state, setState] = useState(false); // ❌ Incorrect
}
Code Example (Correct Usage):
const [state, setState] = useState(false); // ✅ Always at the top level
if (someCondition) {
// Perform conditional logic here
}
What is the purpose of the onChange handler in forms?
The onChange
handler listens for user interactions with form elements. When a user updates a field, the event is captured, and the handler updates the corresponding React state.
function PizzaSizeSelector() {
const [pizzaSize, setPizzaSize] = useState("Medium");
return (
<div>
<label>
Pizza Size:
<select value={pizzaSize} onChange={(e) => setPizzaSize(e.target.value)}>
<option value="Small">Small</option>
<option value="Medium">Medium</option>
<option value="Large">Large</option>
</select>
</label>
<p>Selected size: {pizzaSize}</p>
</div>
);
}
Why shouldn't you use change handlers on non-form elements like div tags?
Adding change handlers on non-form elements like <div>
can lead to accessibility issues. Instead, use form elements (<input>
, <select>
, etc.), which are accessible to assistive technologies.
Key Points:
// Correct
<label>
Select a pizza size:
<select value={size} onChange={(e) => setSize(e.target.value)}>
<option value="Small">Small</option>
<option value="Medium">Medium</option>
<option value="Large">Large</option>
</select>
</label>
// Avoid this pattern
<div onChange={(e) => setSize(e.target.value)}>Select size here</div>
What is useEffect in React?
useEffect
is a React Hook that allows you to handle side effects in functional components. Side effects are operations that interact with the outside world or affect something outside the component's scope, such as:
Key Points:
import React, { useState, useEffect } from 'react';
function DataFetcher() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => setData(data));
}, []); // Empty array means it runs only once
return <div>Data: {data ? JSON.stringify(data) : 'Loading...'}</div>;
}
Why can’t useEffect callbacks be async and how do you handle asynchronous code in useEffect?
The useEffect
callback cannot be async
because React expects the callback to return either nothing or a cleanup function, not a Promise
. To handle asynchronous code, define the async function inside the effect or outside it and then call it.
import React, { useState, useEffect } from 'react';
function AsyncExample() {
const [data, setData] = useState(null);
useEffect(() => {
async function fetchData() {
const res = await fetch('/api/data');
const json = await res.json();
setData(json);
}
fetchData(); // Call the async function
}, []); // Dependency array ensures it runs once
return <div>{data ? JSON.stringify(data) : 'Loading...'}</div>;
}
What are cleanup functions in useEffect, and when are they used?
Cleanup functions in useEffect are used to clean up resources like subscriptions, timers, or API calls when the component unmounts or before the effect re-runs.
Key Points:
import React, { useState, useEffect } from 'react';
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const interval = setInterval(() => setSeconds(s => s + 1), 1000);
return () => clearInterval(interval); // Cleanup on unmount
}, []); // Empty array ensures it sets up the interval only once
return <p>Seconds elapsed: {seconds}</p>;
}
Why does React's useEffect sometimes cause endless re-renders?
If useEffect
is missing its dependency array or the dependencies aren't correctly specified, it may trigger a re-render loop. This happens because updating state in the effect causes another render, which calls the effect again, and so on.
Key Points:
import { useState, useEffect } from "react";
function PizzaComponent() {
const [pizzaSize, setPizzaSize] = useState("medium");
const [price, setPrice] = useState(null);
useEffect(() => {
// This runs whenever pizzaSize changes
console.log(`Fetching price for ${pizzaSize} pizza`);
setPrice(fetchPriceForSize(pizzaSize)); // Assume fetchPriceForSize is defined
}, [pizzaSize]); // Dependency array prevents unnecessary calls
return (
<div>
<button onClick={() => setPizzaSize("small")}>Small</button>
<button onClick={() => setPizzaSize("medium")}>Medium</button>
<button onClick={() => setPizzaSize("large")}>Large</button>
<p>Price: {price}</p>
</div>
);
}
How can you handle loading states when fetching data in React?
Use a loading
state variable to indicate whether data is being fetched. Conditionally render content based on this variable.
Key Points:
import { useState, useEffect } from "react";
function PizzaLoader() {
const [loading, setLoading] = useState(true);
const [pizza, setPizza] = useState(null);
useEffect(() => {
setTimeout(() => {
// Simulate fetching data
setPizza({ name: "Pepperoni", size: "medium", price: 15 });
setLoading(false);
}, 2000);
}, []);
return (
<div>
{loading ? (
<p>Loading pizza...</p>
) : (
<p>
Pizza: {pizza.name} - {pizza.size} (${pizza.price})
</p>
)}
</div>
);
}
How can you avoid over-fetching in useEffect?
Ensure that your useEffect
dependency array is accurate and only includes variables that need to trigger the effect. This prevents unnecessary API calls.
Key Points:
import { useState, useEffect } from "react";
function PizzaSizeTracker() {
const [size, setSize] = useState("medium");
const [price, setPrice] = useState(null);
useEffect(() => {
// Only fetch when size changes
fetch(`/api/pizza-price?size=${size}`)
.then((res) => res.json())
.then((data) => setPrice(data.price));
}, [size]);
return (
<div>
<select value={size} onChange={(e) => setSize(e.target.value)}>
<option value="small">Small</option>
<option value="medium">Medium</option>
<option value="large">Large</option>
</select>
<p>Price: {price}</p>
</div>
);
}
What is the purpose of the key prop in React?
The key
prop helps React identify which elements have changed, been added, or removed. It improves the efficiency of rendering by preventing React from unnecessarily tearing down and rebuilding components.
Why it's important:
How to use it:
const pizzas = [
{ id: 1, name: "Margherita" },
{ id: 2, name: "Pepperoni" },
{ id: 3, name: "Veggie" },
];
function PizzaList() {
return (
<ul>
{pizzas.map((pizza) => (
<li key={pizza.id}>{pizza.name}</li>
))}
</ul>
);
}
Why shouldn't we use the index of an array as the key?
Using the index as a key can cause rendering bugs because the key is not stable. If the list changes (e.g., items are reordered, added, or removed), React might reuse incorrect elements, leading to visual or data-related errors.
Problems with index-based keys:
const toppings = ["Cheese", "Pepperoni", "Mushrooms"];
function ToppingList() {
return (
<ul>
{toppings.map((topping, index) => (
<li key={index}>{topping}</li>
))}
</ul>
);
}
// If the list changes order dynamically, React might retain incorrect associations.
Are there any cases where using the index as a key is acceptable?
Using the index as a key can be acceptable in specific cases, but it should be avoided if better options are available. Acceptable scenarios include:
function StaticList() {
const items = ["One", "Two", "Three"];
return (
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li> // Using index as a key for a static list.
))}
</ul>
);
}
What happens if you don't provide a key?
React will throw a warning in the console and may not optimize rendering. The absence of a key can lead to unnecessary re-rendering or incorrect updates of elements.
function NoKeyList() {
const fruits = ["Apple", "Banana", "Cherry"];
return (
<ul>
{fruits.map((fruit) => (
<li>{fruit}</li> // Missing key will throw a warning.
))}
</ul>
);
}
What is Strict Mode in React?
Strict Mode is a development-only feature in React that enables additional checks and warnings for your code.
import React, { StrictMode } from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
<StrictMode>
<App />
</StrictMode>,
document.getElementById('root')
);
What is a custom hook in React?
A custom hook is a reusable function in React that encapsulates stateful logic using other hooks. It allows you to extract and reuse code logic across components.
Key points:
How do you create a custom hook?
You create a custom hook by writing a function that uses other hooks inside it. Then, you export and reuse the hook in your components.
Example: Creating a custom hook for fetching "Pizza of the Day" data.
import { useState, useEffect } from 'react';
export const usePizzaOfTheDay = () => {
const [pizzaOfTheDay, setPizzaOfTheDay] = useState(null);
useEffect(() => {
const fetchPizzaOfTheDay = async () => {
const response = await fetch('/api/pizza-of-the-day');
const data = await response.json();
setPizzaOfTheDay(data);
};
fetchPizzaOfTheDay();
}, []);
return pizzaOfTheDay;
};
Why use custom hooks?
Custom hooks provide these benefits:
How do you use a custom hook in a component?
Import the custom hook and call it like any other function. Use the returned values or state in the component.
Example: Displaying "Pizza of the Day" using a custom hook.
import { usePizzaOfTheDay } from './usePizzaOfTheDay';
const PizzaOfTheDay = () => {
const pizzaOfTheDay = usePizzaOfTheDay();
if (!pizzaOfTheDay) {
return <div>Loading...</div>;
}
return (
<div className="pizza-of-the-day">
<h3>{pizzaOfTheDay.name}</h3>
<p>{pizzaOfTheDay.description}</p>
<div>From: ${pizzaOfTheDay.sizes[0].price}</div>
<img
src={pizzaOfTheDay.image}
alt={pizzaOfTheDay.name}
className="pizza-of-the-day-image"
/>
</div>
);
};
export default PizzaOfTheDay;
What are the best practices when writing custom hooks?
When should you use custom hooks?
Why should you prefer custom hooks for side effects?
Custom hooks allow side effects (e.g., data fetching, subscriptions) to be isolated from components, making them easier to test and manage. Keeping effects inside custom hooks promotes cleaner, reusable code and improves component performance by avoiding code duplication.
Why do React custom hooks start with the prefix "use"?
The "use" prefix helps React's linting tools automatically detect and enforce hook-specific rules. It signals that the function follows React's hook principles, allowing for better code readability, easier debugging, and ensuring hooks are used correctly within components.
What is useDebugValue Hook?
useDebugValue
is a React hook used for adding custom labels to custom hooks in React DevTools, helping developers quickly identify and debug custom hook values during development.
Key Points:
function usePizzaTracker(initialPizza) {
const [pizzaOfTheDay, setPizzaOfTheDay] = useState(initialPizza);
// Adds a debug value for DevTools
useDebugValue(pizzaOfTheDay ? `${pizzaOfTheDay.id}: ${pizzaOfTheDay.name}` : 'Loading');
return pizzaOfTheDay;
}
Why is onSubmit preferred over onClick for forms?
Using onSubmit
ensures compatibility with keyboard navigation (e.g., pressing Enter to submit) and accessibility tools like screen readers. Forms are better supported natively by browsers when onSubmit
is used.
Key Points:
What is state in React?
State is a local, mutable data store for a React component. It allows a component to keep track of changes and update the UI accordingly.
Key Points:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
How can a child component modify the parent’s state?
A parent can pass a function as a prop to a child. The child can call this function to modify the parent’s state.
Key Points:
function Parent() {
const [message, setMessage] = useState('');
function updateMessage(newMessage) {
setMessage(newMessage);
}
return (
<div>
<Child onSendMessage={updateMessage} />
<p>Message: {message}</p>
</div>
);
}
function Child({ onSendMessage }) {
return (
<button onClick={() => onSendMessage('Hello from Child!')}>
Send Message
</button>
);
}
How do you handle async operations like API calls in React event handlers?
You can define an async function and use await for handling asynchronous tasks like API calls. Always manage loading states to provide feedback to the user.
Key Points:
function Checkout() {
const [loading, setLoading] = useState(false);
const [cart, setCart] = useState(['item1', 'item2']);
async function handleCheckout() {
setLoading(true);
try {
const response = await fetch('/api/order', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ cart }),
});
if (response.ok) {
setCart([]);
alert('Checkout successful!');
} else {
alert('Checkout failed.');
}
} catch (error) {
alert('An error occurred.');
} finally {
setLoading(false);
}
}
return (
<div>
<button onClick={handleCheckout} disabled={loading}>
{loading ? 'Processing...' : 'Checkout'}
</button>
</div>
);
}
What is React Context, and when should it be used?
React Context is a feature that allows you to share state and data across the component tree without passing props manually through every level. It's useful for managing app-level state, such as user data, theme settings, or other global configurations.
When to use Context:
Avoid Context when:
What are the downsides of overusing Context?
Overusing Context can complicate the app's maintenance due to its global nature. Key challenges include:
How do you create and use Context in a React application?
1. Create Context:
import React, { createContext } from 'react';
export const UserContext = createContext(null);
2. Provide Context:
const App = () => {
const [user, setUser] = useState({ name: "Alice", loggedIn: true });
return (
<UserContext.Provider value={{ user, setUser }}>
<Header />
<Main />
</UserContext.Provider>
);
};
3. Consume Context:
import React, { useContext } from 'react';
import { UserContext } from './contexts/UserContext';
const Header = () => {
const { user } = useContext(UserContext);
return <div>Welcome, {user.name}</div>;
};
What is the useContext hook in React?
The useContext
hook allows you to consume a React context in a functional component, providing access to the context's value without needing to wrap components with Context.Consumer
.
Key Points:
// Context creation
import React, { createContext, useContext, useState } from 'react';
const CartContext = createContext();
export const CartProvider = ({ children }) => {
const [cart, setCart] = useState([]);
return (
<CartContext.Provider value={{ cart, setCart }}>
{children}
</CartContext.Provider>
);
};
// Using the context in a component
const Header = () => {
const { cart } = useContext(CartContext); // Access cart from context
return <div>Items in Cart: {cart.length}</div>;
};
const App = () => (
<CartProvider>
<Header />
</CartProvider>
);
export default App;
What are some common issues with the Context API?
Reinforcement Points:
What is a Single Page Application (SPA)?
A Single Page Application (SPA) is a web app that dynamically updates the page content without reloading the entire page. SPAs provide a faster, more seamless user experience.
Key Features:
What is lazy loading in routing, and why is it useful?
Lazy loading is a technique where route components are loaded only when the user navigates to them. This helps reduce the initial load time by splitting the code into smaller chunks.
Key Benefits:
What is the difference between <Link> and <a> in routing?
<Link>
is used in React routing to handle client-side navigation, preserving the application state and avoiding full page reloads, while <a>
triggers a full-page reload.
Advantages of <Link>
:
import { Link } from '@tanstack/react-router';
<Link to="/order">Order</Link> {/* Client-side navigation */}
<a href="/order">Order</a> {/* Full-page reload */}
What is a React Portal?
A React Portal is a special way to render child components outside of the main app root. Instead of limiting your rendered elements to a single DOM node (like #root
), portals let you place content in other parts of the HTML document.
Why use them?
// Example of a custom Portal component
import React from 'react';
import ReactDOM from 'react-dom';
function MyPortal({ children }) {
const wrapperRef = React.useRef(null);
// Create a new div for this portal if it doesn't already exist
if (!wrapperRef.current) {
wrapperRef.current = document.createElement('div');
}
React.useEffect(() => {
const portalRoot = document.getElementById('my-portal-root');
portalRoot.appendChild(wrapperRef.current);
// Cleanup when component unmounts
return () => {
portalRoot.removeChild(wrapperRef.current);
};
}, []);
// Render children into the new div
return ReactDOM.createPortal(children, wrapperRef.current);
}
export default MyPortal;
Why would I use a Portal instead of simply rendering a modal in my main app?
Portals help you cleanly separate the logic for your main app from UI elements that need special positioning. By rendering in a different DOM node (like a dedicated modal root), you avoid tricky CSS or nesting issues.
Key benefits:
What are Class Components in React?
Class components are an older way of writing React components using JavaScript classes. They can hold their own state (using this.setState
), manage lifecycle methods (like componentDidMount
), and render JSX in a render()
method.
Key Points:
import React, { Component } from "react";
class Welcome extends Component {
// Initialize state directly (class field syntax)
state = {
username: "JohnDoe",
};
// Lifecycle method (runs after component is mounted)
componentDidMount() {
console.log("Welcome Component Mounted");
}
// Update the state using this.setState
handleNameChange = () => {
this.setState({ username: "JaneSmith" });
};
render() {
return (
<div>
<h1>Hello, {this.state.username}!</h1>
<button onClick={this.handleNameChange}>Change Name</button>
</div>
);
}
}
export default Welcome;
What Is an Error Boundary in React?
An Error Boundary is a special React component that catches JavaScript errors in its child component tree, logs them, and displays a fallback UI instead of crashing the entire app. As of now, only class components can be true error boundaries.
Key Points:
import React, { Component } from "react";
class SafeWrapper extends Component {
state = { hasError: false };
// Update state if an error is thrown
static getDerivedStateFromError() {
return { hasError: true };
}
// Log error info (e.g., to a service)
componentDidCatch(error, info) {
console.error("SafeWrapper caught an error:", error, info);
}
render() {
if (this.state.hasError) {
return (
<div>
<h2>Oops, something went wrong!</h2>
<p>Try refreshing the page or come back later.</p>
</div>
);
}
return this.props.children;
}
}
export default SafeWrapper;
Why Use Error Boundaries?
Error Boundaries let you catch errors in the component tree and show a fallback UI instead of breaking your entire React application. This improves user experience because your app won’t completely crash when one component fails.
Key Points:
Can We Use Hooks in Class Components?
No, you cannot use hooks (like useState
, useEffect
) inside class components. Hooks only work in functional components. Class components rely on lifecycle methods and this.setState
instead.
Key Points:
How Do Lifecycle Methods in Class Components Compare to Hooks?
Class components use lifecycle methods
for actions that happen at specific times (mount, update, unmount), whereas function components use hooks (like useEffect
) for similar behavior.
Key Points:
class DemoLifecycle extends React.Component {
state = { count: 0 };
componentDidMount() {
console.log("Mounted - similar to useEffect with empty dependencies");
}
componentDidUpdate(prevProps, prevState) {
if (prevState.count !== this.state.count) {
console.log("Updated - similar to useEffect that watches 'count'");
}
}
componentWillUnmount() {
console.log("Cleanup - similar to the return function in useEffect");
}
render() {
return (
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Clicked {this.state.count} times
</button>
);
}
}
What Is getDerivedStateFromError?
getDerivedStateFromError
is a static lifecycle method in a class component. It runs when a child component throws an error, letting you update the component’s state (e.g., hasError: true
) before rendering the fallback UI.
Key Points:
How Can We “Combine” Hooks with a Class Component?
You can’t directly use hooks inside a class component. However, you can wrap the class component with a small function component
that uses the hook, then pass the hook’s returned values down to the class as props.
Key Points:
import React, { Component } from "react";
import useMealOfTheDay from "./useMealOfTheDay"; // A custom hook
function MealBridge(props) {
const meal = useMealOfTheDay();
return <MealClassComponent meal={meal} {...props} />;
}
class MealClassComponent extends Component {
render() {
return (
<div>
<h1>Today's Meal: {this.props.meal}</h1>
{/* Class component logic */}
</div>
);
}
}
export { MealBridge, MealClassComponent };