React Interview Questions
Published by
sanya sanya
1 Implement useUpdateEffect() that it works the same as useEffect() except that it skips running the callback on first render.
import React, { useEffect, useRef, EffectCallback, DependencyList } from 'react'; export function useUpdateEffect(effect: EffectCallback, deps?: DependencyList) { const isFirstRender = useRef(true); useEffect(() => { if (isFirstRender.current) { isFirstRender.current = false; return; } return effect(); }, deps); }
2 Click above header menu on this page, you can see that the dropdown menu is dismissed after clicking outside.
import { useEffect, useRef } from 'react'; export function useClickOutside(callback: () => void) { const ref = useRef<HTMLElement | null>(null); useEffect(() => { function handleClickOutside(event: MouseEvent) { if (ref.current && !ref.current.contains(event.target as Node)) { callback(); } } document.addEventListener('mousedown', handleClickOutside); return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, [callback]); return ref; }
3 When we handle async requests in React, we need to pay attention if the component is already unmounted. implement useIsMounted() for us to easily tell if the component is still not unmounted.
import { useEffect, useRef } from 'react'; export function useIsMounted(): () => boolean { const isMountedRef = useRef(false); useEffect(() => { isMountedRef.current = true; return () => { isMountedRef.current = false; }; }, []); return () => isMountedRef.current; }
4 Create a PhoneNumberInput component. only accepts numerical digits format the number automatically as (123)456-7890 by adding the parenthesis when the 4th digit is entered also adding - before 7th digit
import React, { useState } from 'react'; export function PhoneNumberInput() { const [phoneNumber, setPhoneNumber] = useState(''); const handleInputChange = (e) => { let value = e.target.value.replace(/\D/g, ''); // Remove non-numeric characters if (value.length > 10) value = value.slice(0, 10); // Limit input to 10 digits let formattedNumber = value; if (value.length > 3) { formattedNumber = `(${value.slice(0, 3)})${value.slice(3)}`; // Format first part as (XXX) } if (value.length > 6) { formattedNumber = `(${value.slice(0, 3)})${value.slice(3, 6)}-${value.slice(6)}`; // Add a dash for XXX-XXXX } setPhoneNumber(formattedNumber); }; return ( <input data-testid="phone-number-input" value={phoneNumber} onChange={handleInputChange} /> ); }
5 counter starts from 0.
click the '+' button to increment.
click the '-' button to decrement.
import React, { useState } from 'react'; export function App() { const [count, setCount] = useState(0); const increment = () => setCount(count + 1); const decrement = () => setCount(count - 1); return ( <div> <button data-testid="decrement-button" onClick={decrement}>-</button> <button data-testid="increment-button" onClick={increment}>+</button> <p>Clicked: {count}</p> </div> ); }
6 Implement useToggle() to make things easier
import { useState, useCallback } from 'react'; export function useToggle(initialState = false) { const [state, setState] = useState(initialState); const toggle = useCallback(() => { setState((prevState) => !prevState); }, []); return [state, toggle]; } export function App() { const [on, toggle] = useToggle(); return ( <div> <button onClick={toggle}> {on ? 'On' : 'Off'} </button> </div> ); }
7 Create a hook to tell if it is the first render.
import React, { useRef, useEffect } from 'react'; export function useIsFirstRender() { const isFirstRender = useRef(true); useEffect(() => { isFirstRender.current = false; }, []); return isFirstRender.current; } function App() { const isFirstRender = useIsFirstRender(); return ( <div> {isFirstRender ? <p>This is the first render.</p> : <p>This is not the first render.</p>} </div> ); }
8 When array is used in React state, we need to deal with actions such as push and remove. Implement useArray() to make life easier.
Implement useArray() to make life easier. import { useState, useCallback } from 'react'; type UseArrayActions<T> = { push: (item: T) => void; removeByIndex: (index: number) => void; }; export function useArray<T>(initialValue: T[]): { value: T[] } & UseArrayActions<T> { const [array, setArray] = useState<T[]>(initialValue); const push = useCallback((item: T) => { setArray((prevArray) => [...prevArray, item]); }, []); const removeByIndex = useCallback((index: number) => { setArray((prevArray) => prevArray.filter((_, i) => i !== index)); }, []); return { value: array, push, removeByIndex, }; }
9 Create a hook usePrevious() to return the previous value, with initial value of `undefined
export function usePrevious<T>(value: T): T | undefined { const ref = useRef<T | undefined>(undefined); useEffect(() => { ref.current = value; }, [value]); return ref.current; }
10 useTimeout() Create a hook to easily use setTimeout(callback, delay). reset the timer if delay changes DO NOT reset the timer if only callback changes
import { useEffect, useRef } from "react"; export function useTimeout(callback: () => void, delay: number | null) { const savedCallback = useRef(callback); // Keep the latest callback reference updated useEffect(() => { savedCallback.current = callback; }, [callback]); // Set up the timeout if delay is provided useEffect(() => { if (delay === null || delay === undefined) return; const tick = () => savedCallback.current(); const id = setTimeout(tick, delay); return () => clearTimeout(id); }, [delay]); }
11 For a frequently changing value like text input you might want to debounce the changes. Implement useDebounce() to achieve this.
import { useState, useEffect } from 'react'; export function useDebounce<T>(value: T, delay: number): T { const [debouncedValue, setDebouncedValue] = useState(value); useEffect(() => { const handler = setTimeout(() => { setDebouncedValue(value); }, delay); return () => { clearTimeout(handler); }; }, [value, delay]); return debouncedValue; } export function App() { const [value, setValue] = useState(''); const debouncedValue = useDebounce(value, 1000); return ( <div> <input type="text" value={value} onChange={(e) => setValue(e.target.value)} placeholder="Type something..." /> <p>Debounced Value: {debouncedValue}</p> </div> ); }
12 implement useEffectOnce() as the name says itself, it runs an effect only once.
import { useEffect, EffectCallback } from 'react'; export function useEffectOnce(effect: EffectCallback) { useEffect(effect, []); } export function App() { useEffectOnce(() => { console.log('Effect is running only once.'); return () => { console.log('Cleanup on component unmount.'); }; }); return <div>useEffectOnce</div>; }
Library
WEB DEVELOPMENT
Basic
Frontend
Backend
Interview Questions
JavaScript Interview Questions
React Interview Questions
Application Based JS Questions
Basic and Advanced JavaScript Questions
SQL INTERVIEW QUESTIONS
PHP Interview Questions
Python Interview Questions
FAANG QUESTIONS