15 Hooks Interview Questions and Answers
Prepare for your React interview with this guide on Hooks, featuring common questions and detailed answers to enhance your understanding and skills.
Prepare for your React interview with this guide on Hooks, featuring common questions and detailed answers to enhance your understanding and skills.
Hooks have revolutionized the way developers write React applications by allowing functional components to manage state and side effects. Introduced in React 16.8, Hooks provide a more intuitive and streamlined approach to handling component logic, making code more readable and maintainable. They eliminate the need for class components, thereby simplifying the component structure and reducing boilerplate code.
This article offers a curated selection of interview questions focused on Hooks, designed to help you demonstrate your understanding and proficiency in this essential React feature. By reviewing these questions and their detailed answers, you’ll be better prepared to showcase your expertise and problem-solving abilities in your next technical interview.
The useState
Hook allows you to add state to functional components in React. It returns an array with the current state value and a function to update it, making state management more concise compared to class components.
Example:
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> ); }
In this example, useState
creates a state variable count
and a function setCount
to update it. The initial state is 0. Clicking the button updates the state, causing the component to re-render.
The useContext Hook accesses the value of a context directly in a functional component, simplifying the process without needing a Consumer component.
Example:
import React, { createContext, useContext } from 'react'; const MyContext = createContext(); const MyProvider = ({ children }) => { const value = 'Hello, World!'; return ( <MyContext.Provider value={value}> {children} </MyContext.Provider> ); }; const MyComponent = () => { const contextValue = useContext(MyContext); return <div>{contextValue}</div>; }; const App = () => ( <MyProvider> <MyComponent /> </MyProvider> ); export default App;
The useReducer Hook manages state, especially when logic is complex. It takes a reducer function and an initial state, returning the current state and a dispatch function.
Example:
import React, { useReducer } from 'react'; const initialState = { count: 0 }; function reducer(state, action) { switch (action.type) { case 'increment': return { count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; default: throw new Error(); } } function Counter() { const [state, dispatch] = useReducer(reducer, initialState); return ( <div> <p>Count: {state.count}</p> <button onClick={() => dispatch({ type: 'increment' })}>+</button> <button onClick={() => dispatch({ type: 'decrement' })}>-</button> </div> ); }
In this example, useReducer manages a counter’s state. The reducer function handles state transitions based on action type.
Hooks in React allow you to use state and other features without writing a class. Follow these rules:
The useRef Hook persists values between renders without causing a re-render. It’s commonly used to access and manipulate DOM elements.
import React, { useRef } from 'react'; function FocusInput() { const inputRef = useRef(null); const handleClick = () => { inputRef.current.focus(); }; return ( <div> <input ref={inputRef} type="text" /> <button onClick={handleClick}>Focus Input</button> </div> ); } export default FocusInput;
In React, performance optimization can be achieved using useMemo
and useCallback
. These hooks help prevent unnecessary re-renders by memoizing values and functions.
useMemo
memoizes the result of a computation, recomputing only when dependencies change. This is useful for expensive calculations.
import React, { useMemo } from 'react'; const ExpensiveComponent = ({ num }) => { const computedValue = useMemo(() => { return num * 2; }, [num]); return <div>{computedValue}</div>; };
useCallback
memoizes functions, returning a stable function reference that only changes if dependencies change. This is useful for passing stable function references to child components.
import React, { useCallback } from 'react'; const Button = React.memo(({ onClick }) => { console.log('Button rendered'); return <button onClick={onClick}>Click me</button>; }); const ParentComponent = () => { const handleClick = useCallback(() => { console.log('Button clicked'); }, []); return <Button onClick={handleClick} />; };
The useLayoutEffect Hook fires synchronously after all DOM mutations, useful for reading layout from the DOM and synchronously re-rendering.
Example:
import React, { useLayoutEffect, useRef, useState } from 'react'; function MeasureComponent() { const [dimensions, setDimensions] = useState({ width: 0, height: 0 }); const elementRef = useRef(); useLayoutEffect(() => { const { offsetWidth, offsetHeight } = elementRef.current; setDimensions({ width: offsetWidth, height: offsetHeight }); }, []); return ( <div> <div ref={elementRef} style={{ width: '100px', height: '100px', backgroundColor: 'lightblue' }}> Measure me! </div> <p>Width: {dimensions.width}px, Height: {dimensions.height}px</p> </div> ); } export default MeasureComponent;
The useImperativeHandle
Hook allows you to expose a custom API to parent components using refs, encapsulating internal logic while providing interaction methods.
Example:
import React, { useImperativeHandle, useRef, forwardRef } from 'react'; const CustomInput = forwardRef((props, ref) => { const inputRef = useRef(); useImperativeHandle(ref, () => ({ focus: () => { inputRef.current.focus(); }, clear: () => { inputRef.current.value = ''; } })); return <input ref={inputRef} />; }); const ParentComponent = () => { const inputRef = useRef(); return ( <div> <CustomInput ref={inputRef} /> <button onClick={() => inputRef.current.focus()}>Focus Input</button> <button onClick={() => inputRef.current.clear()}>Clear Input</button> </div> ); };
In this example, CustomInput
uses useImperativeHandle
to expose focus
and clear
methods. ParentComponent
can then use these methods.
In React, the useEffect hook handles side effects in functional components. For asynchronous operations, define an async function inside useEffect and call it immediately to manage the operation within the component’s lifecycle.
Example:
import React, { useEffect, useState } from 'react'; function DataFetchingComponent() { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { let isMounted = true; const fetchData = async () => { try { const response = await fetch('https://api.example.com/data'); const result = await response.json(); if (isMounted) { setData(result); setLoading(false); } } catch (error) { if (isMounted) { console.error('Error fetching data:', error); setLoading(false); } } }; fetchData(); return () => { isMounted = false; }; }, []); if (loading) { return <div>Loading...</div>; } return ( <div> <pre>{JSON.stringify(data, null, 2)}</pre> </div> ); } export default DataFetchingComponent;
In React, the useEffect Hook performs side effects in function components. Use multiple useEffect Hooks in a single component to separate concerns for different side effects, such as fetching data, setting up a subscription, and updating the document title.
Example:
import React, { useState, useEffect } from 'react'; function MyComponent() { const [data, setData] = useState(null); const [count, setCount] = useState(0); useEffect(() => { fetch('https://api.example.com/data') .then(response => response.json()) .then(data => setData(data)); }, []); useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); useEffect(() => { const subscription = someSubscriptionService.subscribe(); return () => { subscription.unsubscribe(); }; }, []); return ( <div> <p>{data ? data.message : 'Loading...'}</p> <button onClick={() => setCount(count + 1)}>Click me</button> </div> ); }
In this example, three useEffect Hooks handle different side effects: fetching data, updating the document title, and setting up a subscription.
The useReducer Hook manages complex state logic in a functional component. It takes a reducer function and an initial state, returning the current state and a dispatch function.
Example:
import React, { useReducer } from 'react'; const initialState = { count: 0 }; function reducer(state, action) { switch (action.type) { case 'increment': return { count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; case 'reset': return initialState; default: throw new Error(); } } function Counter() { const [state, dispatch] = useReducer(reducer, initialState); return ( <div> <p>Count: {state.count}</p> <button onClick={() => dispatch({ type: 'increment' })}>Increment</button> <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button> <button onClick={() => dispatch({ type: 'reset' })}>Reset</button> </div> ); } export default Counter;
The useDeferredValue
Hook defers updates to a value until the browser has had a chance to paint, optimizing performance in scenarios like a search input field.
Example:
import React, { useState, useDeferredValue } from 'react'; function SearchComponent() { const [searchTerm, setSearchTerm] = useState(''); const deferredSearchTerm = useDeferredValue(searchTerm); const handleChange = (e) => { setSearchTerm(e.target.value); }; return ( <div> <input type="text" value={searchTerm} onChange={handleChange} placeholder="Search..." /> <SearchResults searchTerm={deferredSearchTerm} /> </div> ); } function SearchResults({ searchTerm }) { const results = performSearch(searchTerm); return ( <ul> {results.map((result, index) => ( <li key={index}>{result}</li> ))} </ul> ); } function performSearch(query) { return ['Result 1', 'Result 2', 'Result 3'].filter(item => item.toLowerCase().includes(query.toLowerCase())); }
The useRef Hook creates a mutable object that persists for the component’s lifetime. It’s often used for:
Example:
import React, { useRef, useEffect } from 'react'; function TextInputWithFocusButton() { const inputEl = useRef(null); const onButtonClick = () => { inputEl.current.focus(); }; return ( <div> <input ref={inputEl} type="text" /> <button onClick={onButtonClick}>Focus the input</button> </div> ); }
In this example, useRef creates a reference to the input element. Clicking the button focuses the input field without causing a re-render.
To manage state with useState
when dealing with complex objects or arrays, avoid mutating the state directly. Instead, create a new copy of the state and update it.
Example:
import React, { useState } from 'react'; function ComplexStateComponent() { const [state, setState] = useState({ user: { name: 'John', age: 30 }, items: ['item1', 'item2'] }); const updateUserName = (newName) => { setState(prevState => ({ ...prevState, user: { ...prevState.user, name: newName } })); }; const addItem = (newItem) => { setState(prevState => ({ ...prevState, items: [...prevState.items, newItem] })); }; return ( <div> <button onClick={() => updateUserName('Jane')}>Update Name</button> <button onClick={() => addItem('item3')}>Add Item</button> </div> ); }
In this example, the state is an object containing a nested user object and an items array. When updating the user’s name or adding a new item, use the spread operator to create a new copy of the state.
Hook dependencies in React are an array of values that the hook depends on. They are used in hooks like useEffect
, useCallback
, and useMemo
to determine when the hook should be re-executed.
For example, in the useEffect
hook, the dependencies array is the second argument. If any value in this array changes between renders, the effect will re-run.
import React, { useState, useEffect } from 'react'; function ExampleComponent() { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
In this example, the useEffect
hook updates the document title whenever the count
state changes. The dependencies array [count]
ensures that the effect only runs when count
changes.
Managing hook dependencies involves:
useCallback
and useMemo
to optimize performance.