Skip to main content

react-hook-patterns

React hooks are functions that let you “hook into” React state and lifecycle features from function components. Here are ten common React hook patterns that might be asked about in an interview:

1. Basic State Hook

Using useState to add local state to functional components.

function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}

2. Basic Effect Hook

Using useEffect to perform side effects in functional components.

function Example() {
const [count, setCount] = useState(0);

useEffect(() => {
document.title = `You clicked ${count} times`;
});

return <button onClick={() => setCount(count + 1)}>Click me</button>;
}

3. Custom Hook

Creating a custom hook to share logic between components.

function useCustomCounter() {
const [count, setCount] = useState(0);

const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);

return { count, increment, decrement };
}

4. Context Hook

Using useContext to access context values.

const ThemeContext = React.createContext('light');

function Example() {
const theme = useContext(ThemeContext);
return <div>Theme: {theme}</div>;
}

5. Ref Hook

Using useRef to persist values between renders without triggering a re-render.

function Timer() {
const intervalRef = useRef();

useEffect(() => {
const id = setInterval(() => {
// ...your code...
}, 1000);
intervalRef.current = id;
return () => clearInterval(intervalRef.current);
}, []);

return <h1>I am a timer</h1>;
}

6. Reducer Hook

Using useReducer for more complex state logic.

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, { count: 0 });

return (
<>
Count: {state.count}
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
</>
);
}

7. Callback Hook

Using useCallback to memorize callback functions.

function Example({ someProp }) {
const memoizedCallback = useCallback(
() => {
// Perform an action with someProp
},
[someProp],
);

return <ChildComponent onAction={memoizedCallback} />;
}

8. Memo Hook

Using useMemo to memorize computation results.

function Example({ someProp }) {
const memoizedValue = useMemo(() => computeExpensiveValue(someProp), [someProp]);

return <div>{memoizedValue}</div>;
}

9. Imperative Handle Hook

Using useImperativeHandle to expose parent components to methods in a child component.

function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} />;
}
FancyInput = forwardRef(FancyInput);

10. Layout Effect Hook

Using useLayoutEffect to read layout from the DOM and synchronously re-render.

function Example() {
useLayoutEffect(() => {
// Read layout from the DOM and cause a synchronous re-render
});

return <div />;
}

Certainly! Here are five additional hook patterns that are often useful in React development:

11. State with Previous Value

Using useState along with useEffect to keep track of a state's previous value.

function Example() {
const [count, setCount] = useState(0);
const prevCountRef = useRef();

useEffect(() => {
prevCountRef.current = count;
});

const prevCount = prevCountRef.current;

return <h1>Now: {count}, before: {prevCount}</h1>;
}

12. Debounce Hook

Creating a custom useDebounce hook to debounce any fast-changing value.

function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);

useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);

return () => {
clearTimeout(handler);
};
}, [value, delay]);

return debouncedValue;
}

13. Event Listener Hook

Creating a custom useEventListener hook to handle event listeners.

function useEventListener(eventName, handler, element = window) {
const savedHandler = useRef();

useEffect(() => {
savedHandler.current = handler;
}, [handler]);

useEffect(() => {
const isSupported = element && element.addEventListener;
if (!isSupported) return;

const eventListener = event => savedHandler.current(event);

element.addEventListener(eventName, eventListener);

return () => {
element.removeEventListener(eventName, eventListener);
};
}, [eventName, element]);
}

14. Update Effect Hook

Creating a custom useUpdateEffect hook that runs an effect only on updates, not on the initial mount.

function useUpdateEffect(effect, deps) {
const isInitialMount = useRef(true);

useEffect(() => {
if (isInitialMount.current) {
isInitialMount.current = false;
} else {
return effect();
}
}, deps);
}

15. Mounted State Hook

Creating a custom useIsMounted hook to check if the component is still mounted before setting the state to prevent memory leaks.

function useIsMounted() {
const isMounted = useRef(false);

useEffect(() => {
isMounted.current = true;
return () => {
isMounted.current = false;
};
}, []);

return isMounted;
}

Here are five more React hook patterns:

16. Controlled Input Hook

This custom hook manages the state of an input field, making it easier to create controlled inputs.

function useInput(initialValue) {
const [value, setValue] = useState(initialValue);

function handleChange(e) {
setValue(e.target.value);
}

return {
value,
onChange: handleChange,
};
}

// Usage
function MyForm() {
const email = useInput('');

return (
<input type="email" {...email} />
);
}

17. Form Handling Hook

A custom hook for managing form states and handling submissions.

function useForm(initialState) {
const [values, setValues] = useState(initialState);

const handleChange = (e) => {
const { name, value } = e.target;
setValues(prevValues => ({
...prevValues,
[name]: value
}));
};

const handleSubmit = (callback) => (e) => {
e.preventDefault();
callback();
};

return {
values,
handleChange,
handleSubmit
};
}

// Usage
function MyForm() {
const { values, handleChange, handleSubmit } = useForm({ email: '', password: '' });

const submit = () => {
console.log(values);
};

return (
<form onSubmit={handleSubmit(submit)}>
<input name="email" value={values.email} onChange={handleChange} />
<input name="password" value={values.password} onChange={handleChange} />
<button type="submit">Submit</button>
</form>
);
}

18. Theme Switcher Hook

A hook that provides an easy way to toggle a theme, often used in conjunction with context for theme management.

function useThemeSwitcher() {
const [theme, setTheme] = useState('light');

const toggleTheme = () => {
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
};

return {
theme,
toggleTheme
};
}

// Usage
function App() {
const { theme, toggleTheme } = useThemeSwitcher();

return (
<div className={`App ${theme}`}>
<button onClick={toggleTheme}>Toggle Theme</button>
</div>
);
}

19. Async Data Fetch Hook

A hook for fetching data asynchronously, handling loading and error states.

function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
const result = await response.json();
setData(result);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};

fetchData();
}, [url]);

return { data, loading, error };
}

// Usage
function MyComponent() {
const { data, loading, error } = useFetch('https://api.example.com/data');

if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;

return (
<div>{data && data.map(item => <div key={item.id}>{item.title}</div>)}</div>
);
}

20. Timeout Hook

A hook to handle actions after a timeout.

function useTimeout(callback, delay) {
const savedCallback = useRef(callback);

useEffect(() => {
savedCallback.current = callback;
}, [callback]);

useEffect(() => {
if (delay !== null) {
const id = setTimeout(() => savedCallback.current(), delay);
return () => clearTimeout(id);
}
}, [delay]);
}

// Usage
function MyComponent() {
const [count, setCount] = useState(10);

useTimeout(() => setCount(count - 1), count > 0 ? 1000 : null);

return <h1>{count}</h1>;
}

These hooks cover a range of common patterns for handling forms, themes, asynchronous data, and timing in React components.