React.js Logical
React.js Logical Interview Questions and Answers
1. How do you prevent unnecessary re-renders in a large React app?
Explanation:
React re-renders components when props or state change. To avoid unnecessary re-renders, use:
-
React.memo()
to memoize components. -
useMemo()
to memoize computed values. -
useCallback()
to memoize functions.
This prevents React from re-rendering unless necessary, improving performance.
const MemoComponent = React.memo(({ value }) => {
console.log("Rendered");
return <div>{value}</div>;
});
Only re-renders when value
changes.
2. How would you debounce an API call in React?
Explanation:
Debouncing means waiting a short time (like 300ms) before executing a function. It's useful for API calls when typing — so the API is only called after the user stops typing.
const debounce = (fn, delay) => {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
};
const fetchData = debounce((query) => {
fetch(`/api?q=${query}`);
}, 300);
Executes only after 300ms of no input.
3. How to optimize expensive calculations using useMemo
?
Explanation:
If a function takes a lot of time (like looping 1000s of times), React may re-run it on every render. useMemo()
remembers the result until inputs change, saving time.
const result = useMemo(() => expensiveCalc(num), [num]);
4. How do you implement infinite scroll in React?
Explanation:
Infinite scroll means loading more data as you scroll down the page. You can do this by placing a div
at the bottom and using IntersectionObserver
to check when it becomes visible — then fetch more data.
5. How to throttle a button click handler?
Explanation:
Throttling allows a function to run only once in a set time (e.g., once every second), no matter how often the user clicks. This prevents too many fast clicks from triggering the same action.
const throttle = (fn, delay) => {
let last = 0;
return (...args) => {
const now = Date.now();
if (now - last > delay) {
last = now;
fn(...args);
}
};
};
6. Create a toggle switch using useReducer.
Explanation:
Instead of using useState
, you can use useReducer
to toggle between true/false. This makes the logic more maintainable in bigger apps.
Explanation:
Great for toggling boolean values with clear logic separation.
const reducer = (state) => !state;
const [on, toggle] = useReducer(reducer, false);
7. How to preserve component state on unmount?
Explanation:
When a component is removed (unmounted), its state is lost. To keep the data, you can:
-
Move the state to a parent that stays mounted.
-
Save the state in
localStorage
orsessionStorage
and reload it later.
useEffect(() => {
localStorage.setItem("form", JSON.stringify(formData));
}, [formData]);
8. How to implement a global event listener with cleanup?
Explanation:
If you add a global event (like keypress), always remove it when the component unmounts to prevent bugs and memory issues.
Explanation:
Always clean up side effects to prevent memory leaks.
useEffect(() => {
const handleKeyPress = e => console.log(e.key);
window.addEventListener('keypress', handleKeyPress);
return () => window.removeEventListener('keypress', handleKeyPress);
}, []);
9. How to build a custom hook to detect window size?
Explanation:
You can create your own hook using useState
and useEffect
to track window size on resize and use it in any component.
Explanation:
Listens to window resize and updates dimensions.
function useWindowSize() {
const [size, setSize] = useState([window.innerWidth, window.innerHeight]);
useEffect(() => {
const handleResize = () => setSize([window.innerWidth, window.innerHeight]);
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
return size;
}
10. How to debounce inside useEffect for async requests?
Explanation:
This pattern helps you wait until a user stops typing before calling an API, and avoid unnecessary fetch calls during every keystroke.
Explanation:
Debounce inside useEffect avoids unnecessary API hits.
useEffect(() => {
const handler = setTimeout(() => {
if (search) fetchData(search);
}, 300);
return () => clearTimeout(handler);
}, [search]);
11. How to cancel an async request in React when component unmounts?
Explanation:
When a component is removed before an async fetch finishes, it can cause errors. Use AbortController
to stop (abort) the fetch when the component unmounts.
useEffect(() => {
const controller = new AbortController();
fetch('/api/data', { signal: controller.signal });
return () => controller.abort();
}, []);
12. How to conditionally run effects?
Explanation:
You can put a check inside useEffect
to run the effect only when certain conditions are met. This avoids extra fetches or actions.
Explanation:
Check condition inside useEffect
to avoid unwanted runs.
useEffect(() => {
if (!userId) return;
fetch(`/api/user/${userId}`);
}, [userId]);
13. How to delay rendering a component until async logic completes?
Explanation:
While data is loading, show a loading spinner. After data arrives, show the real content. This is common in API-heavy apps.
Explanation:
Use local state to track async status and conditionally render JSX.
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchData().then(() => setLoading(false));
}, []);
return loading ? <Spinner /> : <ActualComponent />;
14. How to compare previous and current props or state?
Explanation:
Use useRef
to store the old value and compare it in the next render. Helpful in debugging or triggering actions only on change.
Explanation:
Use a custom hook or useRef
to store the previous value.
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
15. How to manage focus on an input field after rendering?
Explanation:
Use useRef()
to grab the input element and useEffect()
to focus on it right after the component is mounted.
Explanation:
Use useRef
with useEffect
.
const inputRef = useRef();
useEffect(() => {
inputRef.current.focus();
}, []);
16. How do you fetch data only once when a component mounts?
Explanation:
React’s useEffect
with an empty dependency array ([]
) runs only once — perfect for loading data on initial render.
Explanation:
Using useEffect
with an empty dependency array ensures the effect runs only on first render.
useEffect(() => {
fetch('/api/data')
.then(res => res.json())
.then(setData);
}, []);
17. How to avoid stale closures in React async operations?
Explanation:
A stale closure happens when your async function uses outdated state. To avoid this, use functional updates or a reference (useRef
) to always have the latest value.
const countRef = useRef(count);
useEffect(() => { countRef.current = count; }, [count]);
18. How to handle multiple API calls and wait for all to complete?
Explanation:
Use Promise.all()
to run all API calls at the same time and wait for them to finish. Then update the state with all results together.
useEffect(() => {
Promise.all([
fetch('/api/users').then(res => res.json()),
fetch('/api/posts').then(res => res.json())
]).then(([users, posts]) => {
setUsers(users);
setPosts(posts);
});
}, []);
19. How to lazy load a component?
Explanation:
React.lazy()
lets you load components only when needed. This reduces the initial load time. Wrap it in Suspense
to show a loader until the component is ready.
const LazyComp = React.lazy(() => import('./LazyComp'));
<Suspense fallback={<div>Loading...</div>}>
<LazyComp />
</Suspense>
20. How to implement dark/light mode toggle using context?
Explanation:
Use Context to share theme across components. Manage the theme state using useState
and update it using a toggle function.
const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => setTheme(t => (t === 'light' ? 'dark' : 'light'));
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
21. How to memoize a callback function that depends on props?
Explanation:
Use useCallback()
to save a function so that it doesn’t get recreated unless its dependencies (like props) change. This improves performance.
const handleClick = useCallback(() => {
console.log("Clicked", userId);
}, [userId]);
22. How to combine multiple reducers?
Explanation:
In complex apps, you may have separate pieces of state. Use multiple useReducer
calls or a custom function to organize them cleanly.
const [state1, dispatch1] = useReducer(reducer1, init1);
const [state2, dispatch2] = useReducer(reducer2, init2);
23. How to render a component only once in the lifecycle?
Explanation:
Use useEffect(() => {}, [])
to run logic only once. This is good for setup tasks like initializing state or calling an API on first load.
const [loaded, setLoaded] = useState(false);
useEffect(() => {
if (!loaded) {
doInitLogic();
setLoaded(true);
}
}, [loaded]);
24. How to safely update state after async call?
Explanation:
Sometimes an async request finishes after the component is unmounted. Use a flag to check if it’s still safe to call setState
.
useEffect(() => {
let isMounted = true;
fetch('/api/data').then(res => res.json()).then(data => {
if (isMounted) setData(data);
});
return () => { isMounted = false; };
}, []);
25. How to reset state when route changes?
Explanation:
React Router provides useLocation()
. You can detect route change and reset any form or local state as needed when the path changes.
const location = useLocation();
useEffect(() => {
setForm({}); // reset form when route changes
}, [location.pathname]);
26. How to implement a dynamic tab component using React?
Explanation:
Keep track of which tab is active using useState
, and conditionally render different content based on the selected tab.
const [activeTab, setActiveTab] = useState("home");
return (
<>
<button onClick={() => setActiveTab("home")}>Home</button>
<button onClick={() => setActiveTab("about")}>About</button>
{activeTab === "home" && <Home />}
{activeTab === "about" && <About />}
</>
);
27. How do you persist state between page reloads?
Explanation:
Sometimes you want to keep the app state even if the user reloads the page. You can store the state in localStorage
or sessionStorage
, and load it back on mount.
Explanation:
Use localStorage
or sessionStorage
to save state and retrieve on mount.
const [count, setCount] = useState(() => {
return Number(localStorage.getItem("count")) || 0;
});
useEffect(() => {
localStorage.setItem("count", count);
}, [count]);
28. How to handle keyboard events in a component?
Explanation:
React doesn’t handle keyboard events globally by default. Use useEffect
to attach a listener and clean it up when the component unmounts.
Explanation:
Use useEffect
to listen for keydown
and clean up after unmounting.
useEffect(() => {
const handleKeyDown = (e) => {
if (e.key === 'Escape') closeModal();
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, []);
29. How do you animate component mount/unmount?
Explanation:
Animations make UI smoother. Use libraries like react-transition-group
or CSS transitions to animate components when they appear or disappear.
Explanation:
Use react-transition-group
or CSS animations.
import { CSSTransition } from 'react-transition-group';
<CSSTransition in={show} timeout={300} classNames="fade" unmountOnExit>
<div className="fade">Hello</div>
</CSSTransition>
30. How to prevent a state update on an unmounted component?
Explanation:
When you fetch data, it might finish after the component is gone. To avoid memory leaks or errors, set a flag or use cleanup in useEffect
.
Explanation:
Use a flag inside useEffect
or use a cleanup function.
useEffect(() => {
let isCancelled = false;
fetch('/api/data')
.then(res => res.json())
.then(data => {
if (!isCancelled) setData(data);
});
return () => {
isCancelled = true;
};
}, []);
31. How to dynamically apply CSS classes in React?
Explanation:
React lets you set class names using variables. You can use a ternary (? :
) or helper libraries like classnames
to change styles based on conditions.
const isActive = true;
return <div className={isActive ? "active" : "inactive"}>Hello</div>;
32. How do you manage state in deeply nested components?
Explanation:
Passing props down many levels is messy. Instead, use React Context or global state libraries (like Redux or Zustand) to make state available at any level.
Explanation:
Use Context API or state management libraries like Redux or Zustand.
const MyContext = createContext();
<MyContext.Provider value={value}>
<DeepChild />
</MyContext.Provider>
33. How to implement error boundaries in functional components?
Explanation:
React error boundaries catch runtime errors in rendering. Use a library like react-error-boundary
to do this in functional components.
Explanation:
Wrap children with ErrorBoundary
using react-error-boundary
package or class components.
<ErrorBoundary fallback={<div>Something went wrong</div>}>
<MyComponent />
</ErrorBoundary>
34. How to expose methods from child to parent using useImperativeHandle
?
Explanation:
Sometimes the parent needs to call a method inside the child (like focus). Use useImperativeHandle
and forwardRef
for that.
Explanation:
Use forwardRef
and useImperativeHandle
.
const Child = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
focusInput: () => inputRef.current.focus()
}));
const inputRef = useRef();
return <input ref={inputRef} />;
});
35. How to memoize an expensive component tree?
Explanation:
If a component takes a long time to render and its props don’t change, wrap it in React.memo()
to skip re-rendering.
Explanation:
Wrap the entire component in React.memo
.
const ExpensiveTree = React.memo(() => {
// expensive rendering logic
return <div>Heavy Component</div>;
});
36. How to detect a click outside a component?
Explanation:
Use useRef
to detect if a click was outside a component. This is useful for dropdowns, modals, etc., to close them when clicking elsewhere.
Explanation:
Use useRef
with mousedown
or click
event listener.
useEffect(() => {
const handleClickOutside = e => {
if (ref.current && !ref.current.contains(e.target)) close();
};
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
37. How do you manage app-wide modal visibility?
Explanation:
Use Context or global state to toggle modal visibility from anywhere in the app without prop drilling.
Explanation:
Use React Context or global state to toggle modals.
const ModalContext = createContext();
function App() {
const [modalOpen, setModalOpen] = useState(false);
return (
<ModalContext.Provider value={{ modalOpen, setModalOpen }}>
<Layout />
</ModalContext.Provider>
);
}
38. How to animate list item transitions on state change?
Explanation:
When items are added or removed from a list, animations make the UI feel smoother. Use libraries like framer-motion
or react-transition-group
to animate list changes.
Explanation:
Use react-transition-group
or framer-motion
to animate entering/exiting items.
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}>
{item.text}
</motion.div>
39. How to prevent rerendering of a parent when a child state updates?
Explanation:
Move the state to the child component and wrap it with React.memo()
. This way, the parent doesn’t re-render unnecessarily.
Explanation:
Lift the child component into a memoized wrapper.
const Child = React.memo(({ value }) => <div>{value}</div>);
40. How to load environment-specific config in React?
Explanation:
Use .env
files to store different config values for dev and prod. Prefix with REACT_APP_
to access using process.env.REACT_APP_*
.
Explanation:
Use .env.development
, .env.production
, and access with process.env.REACT_APP_*
.
console.log(process.env.REACT_APP_API_URL);
41. How to implement drag and drop in React?
Explanation:
Use HTML5 drag events like onDragStart
, onDrop
, and onDragOver
, or simplify with libraries like react-beautiful-dnd
for complex UIs.
Explanation:
Use HTML5 drag and drop API or a library like react-beautiful-dnd
.
function handleDrop(e) {
const id = e.dataTransfer.getData("text/plain");
// move logic
}
<div draggable onDragStart={e => e.dataTransfer.setData("text/plain", item.id)}>
{item.name}
</div>
<div onDrop={handleDrop} onDragOver={e => e.preventDefault()} />
42. How to preload data before rendering a route?
Explanation:
You can use route loaders (in React Router v6.4+) or fetch data in a layout component before rendering the route.
Explanation:
Use react-router
loader functions (v6.4+) or trigger data fetch in parent layout.
useEffect(() => {
fetchData().then(setData);
}, [routeId]);
43. How to sync form inputs across multiple components?
Explanation:
Lift the state up to a common parent or use Context to share the form data between components. This way, updates in one place reflect everywhere.
Explanation:
Lift form state to a common parent or use Context.
const [form, setForm] = useState({ name: "", email: "" });
function updateField(field, value) {
setForm(prev => ({ ...prev, [field]: value }));
}
44. How do you pause/resume animations in React?
Explanation:
Use animation state like isPaused
and update animation props conditionally. Libraries like framer-motion
make this easy.
Explanation:
Use CSS classes or state-driven animation libraries like framer-motion
.
<motion.div animate={{ rotate: isPaused ? 0 : 360 }} transition={{ repeat: Infinity }} />
45. How do you enforce prop types in React?
Explanation:
Use the prop-types
package in JavaScript or switch to TypeScript to define strict types for props. This helps catch bugs early.
Explanation:
Use prop-types
package or TypeScript.
import PropTypes from 'prop-types';
Component.propTypes = {
name: PropTypes.string.isRequired,
};
46. How do you create controlled vs uncontrolled components?
Explanation:
Controlled means value is set via state and updated with onChange
. Uncontrolled means DOM handles it. Use useRef
for uncontrolled components.
Explanation:
Controlled: value managed by React. Uncontrolled: DOM manages input state.
// Controlled
<input value={value} onChange={e => setValue(e.target.value)} />
// Uncontrolled
<input ref={inputRef} defaultValue="test" />
47. How to handle race conditions in async requests?
Explanation:
Race conditions happen when multiple async requests overlap. Use an increasing counter or AbortController
to cancel older requests.
Explanation:
Use AbortController or request timestamps to cancel stale responses.
let latest = 0;
const fetchWithRace = async (query) => {
const current = ++latest;
const res = await fetch(`/search?q=${query}`);
if (current === latest) setData(await res.json());
};
48. How to control scroll position manually?
Explanation:
Sometimes you want to scroll to a section of the page (like on button click). Use window.scrollTo()
or ref.scrollIntoView()
to do it programmatically.
Explanation:
Use window.scrollTo()
or ref.scrollIntoView()
.
const ref = useRef();
<button onClick={() => ref.current.scrollIntoView()}>Scroll</button>
49. How to test React components with mock context or props?
Explanation:
In tests, wrap your component inside Context.Provider
or pass mock props directly. This helps simulate real scenarios without relying on actual data.
Explanation:
Use React Testing Library
and wrap component in providers.
render(
<MyContext.Provider value={mockValue}>
<MyComponent />
</MyContext.Provider>
);
50. How to securely handle user input and prevent XSS?
Explanation:
XSS (cross-site scripting) happens when unsafe input is rendered as HTML. Always escape user content or sanitize it before rendering. Never use dangerouslySetInnerHTML
unless absolutely necessary.
Explanation:
Sanitize inputs before rendering and avoid dangerouslySetInnerHTML
.
const safeText = input.replace(/</g, "<").replace(/>/g, ">");
return <div>{safeText}</div>;