Skip
Arish's avatar

36. Intermediate Interview Questions


Intermediate React Interview Questions

Questions for mid-level React developers.


Q1: Explain the React component lifecycle

Answer: Components go through phases: Mounting, Updating, Unmounting.

jsx
1function Component() {
2  // Mounting: Component is created
3  useEffect(() => {
4    console.log('Component mounted')
5    
6    // Unmounting: Cleanup
7    return () => {
8      console.log('Component will unmount')
9    }
10  }, [])
11  
12  // Updating: Dependencies changed
13  useEffect(() => {
14    console.log('Dependencies updated')
15  }, [dep1, dep2])
16}

Class component methods:

  • componentDidMountuseEffect(() => {}, [])
  • componentDidUpdateuseEffect(() => {}, [deps])
  • componentWillUnmountuseEffect(() => cleanup, [])

Q2: What is the difference between useEffect and useLayoutEffect?

Answer:

useEffectuseLayoutEffect
Runs after paintRuns before paint
AsynchronousSynchronous
Won't block visual updatesMay block visual updates
Most common choiceFor DOM measurements/mutations
jsx
1// useEffect - runs after browser paints
2useEffect(() => {
3  // Most side effects go here
4}, [])
5
6// useLayoutEffect - runs before browser paints
7useLayoutEffect(() => {
8  // DOM measurements, prevents flicker
9  const rect = ref.current.getBoundingClientRect()
10}, [])

Q3: How does React.memo work?

Answer: React.memo is a higher-order component that memoizes functional components, preventing re-renders if props haven't changed.

jsx
1const MemoizedComponent = React.memo(function Component({ name }) {
2  console.log('Rendering')
3  return <div>{name}</div>
4})
5
6// Custom comparison
7const MemoizedComponent = React.memo(Component, (prevProps, nextProps) => {
8  return prevProps.id === nextProps.id
9})

Use when:

  • Component renders often with same props
  • Component is expensive to render
  • Parent re-renders frequently

Q4: Explain useMemo vs useCallback

Answer:

jsx
1// useMemo - memoizes a VALUE
2const sortedItems = useMemo(() => {
3  return items.sort((a, b) => a.name.localeCompare(b.name))
4}, [items])
5
6// useCallback - memoizes a FUNCTION
7const handleClick = useCallback(() => {
8  console.log(count)
9}, [count])
10
11// They're equivalent:
12useCallback(fn, deps) === useMemo(() => fn, deps)

Use useMemo for:

  • Expensive calculations
  • Objects/arrays passed as props

Use useCallback for:

  • Event handlers passed to memoized children
  • Dependencies in useEffect

Q5: What are error boundaries?

Answer: Error boundaries catch JavaScript errors in child components and display fallback UI.

jsx
1class ErrorBoundary extends React.Component {
2  state = { hasError: false }
3  
4  static getDerivedStateFromError(error) {
5    return { hasError: true }
6  }
7  
8  componentDidCatch(error, errorInfo) {
9    console.error('Error:', error, errorInfo)
10  }
11  
12  render() {
13    if (this.state.hasError) {
14      return <h1>Something went wrong.</h1>
15    }
16    return this.props.children
17  }
18}
19
20// Usage
21<ErrorBoundary>
22  <MyComponent />
23</ErrorBoundary>

Note: Error boundaries don't catch:

  • Event handler errors
  • Async code errors
  • Server-side rendering errors
  • Errors in the boundary itself

Q6: How do you optimize React performance?

Answer:

  1. Memoization
jsx
1const MemoizedChild = React.memo(Child)
2const value = useMemo(() => compute(a, b), [a, b])
3const handler = useCallback(() => {}, [deps])
  1. Code Splitting
jsx
1const LazyComponent = lazy(() => import('./Component'))
  1. Virtualization (for long lists)
jsx
1import { useVirtualizer } from '@tanstack/react-virtual'
  1. Proper key usage
jsx
1{items.map(item => <Item key={item.id} />)}
  1. Avoid inline objects/functions
jsx
1// ❌ Creates new object every render
2<Child style={{ color: 'red' }} />
3
4// ✅ Stable reference
5const style = useMemo(() => ({ color: 'red' }), [])
6<Child style={style} />

Q7: What are React portals?

Answer: Portals render children into a different DOM node outside the parent hierarchy.

jsx
1import { createPortal } from 'react-dom'
2
3function Modal({ children }) {
4  return createPortal(
5    <div className="modal">{children}</div>,
6    document.body
7  )
8}

Use cases:

  • Modals/dialogs
  • Tooltips
  • Dropdowns
  • Toast notifications

Q8: Explain Context API and its limitations

Answer: Context provides a way to share values between components without prop drilling.

jsx
1const ThemeContext = createContext('light')
2
3function App() {
4  return (
5    <ThemeContext.Provider value="dark">
6      <Page />
7    </ThemeContext.Provider>
8  )
9}
10
11function Button() {
12  const theme = useContext(ThemeContext)
13  return <button className={theme}>Click</button>
14}

Limitations:

  • All consumers re-render when context changes
  • Not suitable for frequently changing data
  • Can cause performance issues if overused

Solutions:

  • Split contexts by update frequency
  • Use memoization
  • Consider state management libraries

Q9: What is reconciliation?

Answer: Reconciliation is React's algorithm for diffing two trees and determining which parts need to be updated.

Key assumptions:

  1. Different element types produce different trees
  2. Keys identify stable children across renders

Process:

  1. Compare root elements
  2. If different types → rebuild entire tree
  3. If same type → update attributes, recurse on children
  4. Use keys to match children efficiently

Q10: How do you handle forms in React?

Answer:

jsx
1// Controlled component
2function Form() {
3  const [form, setForm] = useState({ email: '', password: '' })
4  const [errors, setErrors] = useState({})
5  
6  const handleChange = (e) => {
7    const { name, value } = e.target
8    setForm(prev => ({ ...prev, [name]: value }))
9  }
10  
11  const validate = () => {
12    const newErrors = {}
13    if (!form.email) newErrors.email = 'Required'
14    if (!form.password) newErrors.password = 'Required'
15    setErrors(newErrors)
16    return Object.keys(newErrors).length === 0
17  }
18  
19  const handleSubmit = (e) => {
20    e.preventDefault()
21    if (validate()) {
22      // Submit form
23    }
24  }
25  
26  return (
27    <form onSubmit={handleSubmit}>
28      <input name="email" value={form.email} onChange={handleChange} />
29      {errors.email && <span>{errors.email}</span>}
30      <button type="submit">Submit</button>
31    </form>
32  )
33}

For complex forms, consider: React Hook Form, Formik


Q11: What is the difference between createElement and cloneElement?

Answer:

jsx
1// createElement - creates new element
2const element = React.createElement('div', { className: 'box' }, 'Hello')
3// Equivalent to: <div className="box">Hello</div>
4
5// cloneElement - clones existing element with new props
6const cloned = React.cloneElement(element, { id: 'main' })
7// Adds id="main" to the element

cloneElement use cases:

  • Adding props to children
  • Modifying children in parent

Q12: How do you share logic between components?

Answer:

  1. Custom Hooks (preferred)
jsx
1function useWindowSize() {
2  const [size, setSize] = useState({ width: 0, height: 0 })
3  // ... logic
4  return size
5}
  1. Render Props
jsx
1<DataFetcher url="/api/data">
2  {({ data, loading }) => loading ? <Spinner /> : <List data={data} />}
3</DataFetcher>
  1. Higher Order Components
jsx
1const EnhancedComponent = withAuth(MyComponent)

Custom hooks are the modern, preferred approach!