10 React Hook Interview Questions and Answers
Prepare for your next technical interview with this guide on React Hooks, featuring common questions and answers to enhance your understanding and skills.
Prepare for your next technical interview with this guide on React Hooks, featuring common questions and answers to enhance your understanding and skills.
React Hooks have revolutionized the way developers build and manage state in functional components. By allowing the use of state and other React features without writing a class, Hooks have simplified codebases and improved readability. This modern approach to React development has quickly become a standard practice, making it essential for developers to understand and utilize Hooks effectively.
This article provides a curated selection of interview questions focused on React Hooks. Reviewing these questions will help you deepen your understanding of Hooks, enhance your problem-solving skills, and prepare you to discuss this critical topic confidently in your next technical interview.
useState
Hook to manage state in a functional component?The useState
Hook is a key part of React’s Hooks API, allowing functional components to manage state. It returns an array with the current state value and a function to update it.
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, and clicking the button increments the count.
useEffect
Hook works and provide an example of its usage.The useEffect
Hook manages side effects in React components. It takes a callback function and an optional array of dependencies. The effect runs after the initial render and whenever dependencies change.
Example:
import React, { useState, useEffect } from 'react'; function ExampleComponent() { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; return () => { document.title = 'React App'; }; }, [count]); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
Here, useEffect
updates the document title whenever count
changes, with a cleanup function to reset the title.
useReducer
Hook and when would you use it over useState
?The useReducer
hook is for managing complex state logic in React components. It is an alternative to useState
when state transitions are intricate. 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, with the reducer function handling state transitions.
useCallback
Hook and provide a scenario where it would be useful.The useCallback
Hook memoizes a function, ensuring the same function instance is returned unless dependencies change. This is useful for preventing unnecessary re-renders when passing functions as props.
Example:
import React, { useState, useCallback } from 'react'; const ChildComponent = React.memo(({ onClick }) => { console.log('ChildComponent rendered'); return <button onClick={onClick}>Click me</button>; }); const ParentComponent = () => { const [count, setCount] = useState(0); const handleClick = useCallback(() => { console.log('Button clicked'); }, []); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> <ChildComponent onClick={handleClick} /> </div> ); }; export default ParentComponent;
Here, handleClick
is memoized, preventing ChildComponent
from re-rendering unnecessarily.
Custom Hooks in React are functions that encapsulate reusable logic. They start with “use” and can call other Hooks.
Example:
import { useState, useEffect } from 'react'; function useFetch(url) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { fetch(url) .then(response => response.json()) .then(data => { setData(data); setLoading(false); }); }, [url]); return { data, loading }; } // Usage in a component function App() { const { data, loading } = useFetch('https://api.example.com/data'); if (loading) { return <div>Loading...</div>; } return ( <div> <pre>{JSON.stringify(data, null, 2)}</pre> </div> ); }
In this example, useFetch
encapsulates data fetching logic, returning the data and loading state.
Handling asynchronous operations within a Hook, like fetching data, can be managed using useEffect
with async/await syntax.
Example:
import React, { useState, useEffect } 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;
Sharing state between components can be managed using useContext
. Create a context and provide it at a higher level in your component tree. Components within this tree can access the shared state using useContext
.
Example:
import React, { useState, useContext, createContext } from 'react'; const MyContext = createContext(); const MyProvider = ({ children }) => { const [sharedState, setSharedState] = useState('Initial State'); return ( <MyContext.Provider value={{ sharedState, setSharedState }}> {children} </MyContext.Provider> ); }; const ComponentA = () => { const { sharedState, setSharedState } = useContext(MyContext); return ( <div> <p>Component A: {sharedState}</p> <button onClick={() => setSharedState('Updated State from A')}>Update from A</button> </div> ); }; const ComponentB = () => { const { sharedState } = useContext(MyContext); return <p>Component B: {sharedState}</p>; }; const App = () => ( <MyProvider> <ComponentA /> <ComponentB /> </MyProvider> ); export default App;
Debouncing ensures a function is not called too frequently. In React, this can optimize performance, such as when handling user input events. A custom hook can debounce a function call.
Example:
import { useState, useEffect } from 'react'; function useDebounce(value, delay) { const [debouncedValue, setDebouncedValue] = useState(value); useEffect(() => { const handler = setTimeout(() => { setDebouncedValue(value); }, delay); return () => { clearTimeout(handler); }; }, [value, delay]); return debouncedValue; } // Usage example import React, { useState } from 'react'; function SearchInput() { const [query, setQuery] = useState(''); const debouncedQuery = useDebounce(query, 500); useEffect(() => { if (debouncedQuery) { console.log('Searching for:', debouncedQuery); } }, [debouncedQuery]); return ( <input type="text" value={query} onChange={(e) => setQuery(e.target.value)} placeholder="Search..." /> ); }
useEffect
and useLayoutEffect
and when would you use each?useEffect
and useLayoutEffect
both allow side effects in React components but differ in timing. useEffect
runs after the render is committed, while useLayoutEffect
runs synchronously after DOM mutations but before the browser paints.
Example:
import React, { useEffect, useLayoutEffect, useState } from 'react'; const ExampleComponent = () => { const [count, setCount] = useState(0); useEffect(() => { console.log('useEffect: After render'); }, [count]); useLayoutEffect(() => { console.log('useLayoutEffect: Before paint'); }, [count]); return ( <div> <p>{count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); };
In this example, useEffect
logs after rendering, while useLayoutEffect
logs before the paint.
useImperativeHandle
Hook and provide a use case for it.The useImperativeHandle
hook customizes the instance value exposed to parent components when using ref
. This is useful for controlling child component behavior from the parent without exposing the entire child component instance.
Example:
import React, { useImperativeHandle, useRef, forwardRef } from 'react'; const ChildComponent = forwardRef((props, ref) => { const inputRef = useRef(); useImperativeHandle(ref, () => ({ focus: () => { inputRef.current.focus(); } })); return <input ref={inputRef} />; }); const ParentComponent = () => { const childRef = useRef(); const handleClick = () => { childRef.current.focus(); }; return ( <div> <ChildComponent ref={childRef} /> <button onClick={handleClick}>Focus Input</button> </div> ); }; export default ParentComponent;
In this example, ChildComponent
uses useImperativeHandle
to expose a focus
method to the parent component.