React.js Flashcards

1
Q

What’s the difference between useEffect and useLayoutEffect? When should you use each?

A

Both of these can be used to basically do the same thing but they have slightly different use cases. useEffect allows you to perform side effects such as fetching data or directly updating the DOM, after a component renders.

However, if your effect is mutating the DOM and the DOM mutation will change the appearance of the DOM node between the time that it is rendered and your effect mutates it, then you’ll want to use useLayoutEffect.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

How can you prevent unnecessary re-renders in functional components?

A

React hooks such as React.memo() or useMemo() to memoize computed values so they aren’t recalculated on every render.

Additionally, only store in useState what needs to cause a re-render, and use key props carefully and don’t use dynamic keys that change on every render unless needed.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

What are the rules of hooks, and why are they important?

A

Don’t call Hooks inside loops, conditions, or nested functions.

Always use hooks at the top level of your React function before any early returns.

This ensures that Hooks are called in the same order each time a component renders.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

How does useRef differ from useState? What are common use cases?

A

useState causes re-render, useRef does not.

If your variable is something that decides a view layer render, use useState, otherwise use useRef.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

How can you mimic componentDidMount, componentDidUpdate, and componentWillUnmount with hooks?

A

useEffect can emulate the same behaviour.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

What’s the difference between controlled and uncontrolled components in forms?

A

In most cases, we use controlled components to implement forms.

In a controlled component, form data is handled by a React compenent. In an uncontrolled component, form data is handled by the DOM itself.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

How do you lift state up in React, and when should you do it?

A

When multiple components need to reflect the same changing data, so we lift the shared state to their closest comment ancestor.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

What are compound components in React and how are they built?

A

Compound components exist by having two or more components work together to achieve a useful task.

Typically, one component is a parent and the other is a child.

The objective is to provide a more expressive and flexible API.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

How would you structure a large-scale React application?

A

By following a modular approach.

This means breadking down the application into smaller, manageable components.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

What are some strategies to manage deeply nested component trees?

A

Deeply nested component trees often result from overly rigid hierarchies. Instead of nesting components directly inside each other, consider passing components as props or composing them using layout or slot-based patterns.

This reduces the visual and logical depth of the tree and improves readability.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

When should you use Context API vs Redux/Zustand/Recoil?

A

Context API is great for simple, low-frequency, app-wide state; for more complex or performance-sensitive needs, tools like Redux, Zustand, or Recoil are better suited.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

How does React’s useReducer hook work, and when should you prefer it over useState?

A

Both useState and useReducer are React hooks used to manage state within functional components, but they differ in their approach to state updates.

useState is simpler for basic state management where updates are straightforward, while useReducer is better suited for more complex state logic, especially when dealing with multiple sub-values or when the next state depends on the previous state.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

What are the downsides of using Context for frequent state updates?

A

Using Context for frequent state updates causes all consuming components to re-render on every change, even if they only use part of the state. This leads to performance issues, especially in large or deeply nested trees.

Context lacks built-in selectors or fine-grained subscriptions, making it harder to optimize or scale for high-frequency updates.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

How would you persist state across sessions in a React app?

A

To persist state across sessions in a React app, you can store it in localStorage, sessionStorage, or IndexedDB, and then restore it when the app initializes.

A common pattern is to sync React state with localStorage using useEffect and useState. For more complex apps, libraries like Redux Persist, Zustand with middleware, or React Query with hydration provide more structured and scalable solutions.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

How can you implement global state without external libraries?

A

You can implement global state in React without external libraries by using the Context API combined with useReducer or useState.

Create a context, wrap your app in a provider component, and manage shared state inside that provider. Components can then access and update the state using useContext, making it available app-wide without prop drilling.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

What is React.memo and how does it help with performance?

A

React.memo is a higher-order component that memoizes a functional component—meaning it prevents unnecessary re-renders if the component’s props haven’t changed. It works by doing a shallow comparison of the props and only re-rendering if there’s a difference.

This helps improve performance in components that receive the same props frequently, especially in large or complex UIs.

17
Q

What is the purpose of useCallback and useMemo, and when should you avoid them?

A

useCallback and useMemo are React hooks used to optimize performance by memoizing functions and values between renders.

useCallback(fn, deps) returns a memoized function—use it to prevent unnecessary re-creations of a function passed as a prop.

useMemo(fn, deps) returns a memoized value—use it to avoid recalculating expensive computations on every render.

When to Avoid:

Avoid them if the function or value is cheap to compute or the component doesn’t re-render frequently. Overusing them can actually add complexity and hurt performance due to the cost of maintaining the memoization itself. Use them only when profiling shows that unnecessary re-renders or recalculations are a real issue.

18
Q

How do you profile performance in a React app?

A

You can profile a React app using the React DevTools Profiler, which shows which components rendered, how long they took, and why they re-rendered. Tools like why-did-you-render help detect unnecessary renders, and browser dev tools can benchmark custom logic.

Profiling helps identify real performance issues before applying optimizations like memoization.

19
Q

What are the main causes of performance bottlenecks in React apps?

A

The main causes of performance bottlenecks in React apps include unnecessary re-renders, especially from changing props or state that trigger updates across many components.

Large component trees, expensive calculations during rendering, and frequent updates (like animations or input handling) can also slow things down.

Poorly managed global state or overuse of context in high-frequency scenarios can make the problem worse.

20
Q

How does reconciliation work in React, and how can you optimize it?

A

Reconciliation in React is the process of comparing the current virtual DOM with the previous one to determine the minimal set of changes needed to update the real DOM efficiently. React uses a diffing algorithm that compares elements by type and key; if something has changed, it updates only that part of the DOM.

To optimize reconciliation:

Use stable key props in lists to help React track elements accurately.

Avoid unnecessary re-renders with React.memo, useMemo, and useCallback.

Split large components into smaller ones to limit the scope of re-renders and improve diffing efficiency.
21
Q

How would you handle loading, error, and success states in data fetching?

A

To handle loading, error, and success states in data fetching in React, you can use useState for managing the states and useEffect to trigger the data fetching process. Here’s a typical pattern:

Loading state: Set an initial state to true when starting the request.

Success state: Update the state with the fetched data once the request is successful.

Error state: Catch errors and store the error message or object.
22
Q

What’s the best way to cancel an ongoing API request in a component?

A

The best way to cancel an ongoing API request in a React component is to use an abort controller (via the AbortController API) within the useEffect hook. This allows you to cancel the request if the component unmounts or if you need to stop the request before completion (e.g., when navigating away from the page).

Key Steps:

AbortController: Creates a controller that can signal the fetch request to abort.

Signal: Passed to the fetch API to associate it with the controller.

Cleanup: Inside the useEffect cleanup function, we call controller.abort() to cancel the request if the component is unmounted.

This ensures that if the component is unmounted before the fetch completes, the request will be canceled, preventing potential memory leaks or errors.

23
Q

How does React Query / SWR improve the data fetching experience?

A
  • React Query is a feature-rich solution for managing server state with caching and syncing capabilities.
  • SWR is a simpler library focused on fast and efficient data fetching with caching and background revalidation.

React Query and SWR simplify data fetching by automatically caching responses, reducing redundant API calls and improving performance. They handle background refetching to ensure data is always up-to-date without manual intervention. Both libraries provide built-in error handling and retries, making API requests more reliable.

They also offer efficient solutions for managing server state, like pagination and infinite scrolling, reducing the need for custom state management.

24
Q

How can you debounce or throttle API calls in React?

A

To debounce API calls in React, use lodash.debounce or setTimeout to delay the API request until the user stops interacting for a specified time.

Throttling can be done using lodash.throttle or setInterval to limit API calls to a certain frequency, regardless of user actions.

Both methods improve performance by reducing unnecessary requests during frequent user interactions, like typing or scrolling.

25
What’s the difference between client-side and server-side rendering in React apps?
Client-side rendering (CSR) means that the browser downloads a minimal HTML page and uses JavaScript to render the app's content. This leads to faster initial load times after the JavaScript is loaded but can have slower first-page render and SEO challenges. Server-side rendering (SSR) means that the server generates the HTML content for each page request and sends it to the browser. This improves the first-page load time, SEO, and allows content to be visible without waiting for JavaScript, but may require more server resources and setup. In short: CSR relies on the browser to render the page, while SSR renders the page on the server and sends a fully-formed HTML response.
26
What are render props, and how do they differ from higher-order components (HOCs)?
Render props is a pattern where a component accepts a function as a prop to control what gets rendered based on its state or logic, allowing for more flexible customization. Higher-order components (HOCs) are functions that take a component and return a new one, adding additional functionality or state management to the original component. The main difference is that render props pass logic through a function prop for customization, while HOCs wrap components to enhance them. Render props provide more direct control over rendering, while HOCs can lead to more complex nesting when combining multiple HOCs.
27
What are custom hooks, and how do you ensure they remain reusable and generic?
Custom hooks in React are JavaScript functions that allow you to reuse stateful logic across multiple components. To ensure they remain reusable and generic, avoid hardcoding component-specific logic and use parameters to customize behavior. Return only the necessary state and functions, keeping the hook's API clean. By focusing on logic separation and flexibility, custom hooks can be shared across different components in a modular way.
28
How would you implement error boundaries in a modern React app?
To implement error boundaries in a modern React app, create a class component with getDerivedStateFromError to catch errors and update the state, and componentDidCatch for logging the error. T he error boundary component renders a fallback UI when an error is caught in its children. Wrap parts of your app with the error boundary to catch errors in specific components or sections. This ensures that React apps don’t crash entirely, offering a better user experience during runtime failures.
29
What’s the difference between lazy loading with React.lazy and dynamic imports with Next.js?
React.lazy is a React feature for client-side lazy loading of components, using Suspense to handle loading states but is limited to SPAs. Next.js dynamic imports use next/dynamic and provide more features, including server-side rendering (SSR) support, automatic code splitting, and custom loading components. While React.lazy is simple and client-side focused, Next.js offers advanced functionality for SSR and optimized performance across the app.