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 or sessionStorage 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, "&lt;").replace(/>/g, "&gt;");
return <div>{safeText}</div>;

Scroll to Top