React Hooks revolutionized the way developers write functional components by allowing them to use state, lifecycle methods, and other React features without writing a class. Since their introduction in React 16.8, hooks have become an essential part of modern React development. In this article, we will explore all the built-in React hooks, explain their purpose, and provide TypeScript examples to demonstrate their usage.
1. useState
The useState hook is the most commonly used hook in React. It allows functional components to manage state.
Syntax:
const [state, setState] = useState<Type>(initialState);
Example:
import React, { useState } from 'react';
const Counter: React.FC = () => {
const [count, setCount] = useState<number>(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
export default Counter;
Explanation:
-
useStatetakes an initial state value and returns an array with two elements: the current state and a function to update it. - In the example,
countis the state variable, andsetCountis the function to update it.
2. useEffect
The useEffect hook is used to perform side effects in functional components, such as fetching data, subscribing to events, or manually changing the DOM.
Syntax:
useEffect(() => {
// Side effect logic
return () => {
// Cleanup logic
};
}, [dependencies]);
Example:
import React, { useState, useEffect } from 'react';
const Timer: React.FC = () => {
const [seconds, setSeconds] = useState<number>(0);
useEffect(() => {
const interval = setInterval(() => {
setSeconds((prev) => prev + 1);
}, 1000);
return () => clearInterval(interval); // Cleanup on unmount
}, []);
return <p>Seconds: {seconds}</p>;
};
export default Timer;
Explanation:
- The
useEffecthook runs after every render by default. - The second argument is an array of dependencies. If any dependency changes, the effect runs again.
- If you return a function from
useEffect, it will be used for cleanup (e.g., clearing timers or unsubscribing from events).
3. useContext
The useContext hook allows you to access the value of a React context without wrapping your component in a Context.Consumer.
Syntax:
const value = useContext<ContextType>(MyContext);
Example:
import React, { useContext } from 'react';
const ThemeContext = React.createContext<string>('light');
const ThemedComponent: React.FC = () => {
const theme = useContext(ThemeContext);
return <p>Current Theme: {theme}</p>;
};
export default ThemedComponent;
Explanation:
-
useContexttakes a context object and returns its current value. - The value is determined by the nearest
Context.Providerabove the component in the tree.
4. useReducer
The useReducer hook is an alternative to useState for managing complex state logic. It is inspired by Redux and uses a reducer function to handle state transitions.
Syntax:
const [state, dispatch] = useReducer<Reducer<State, Action>>(reducer, initialState);
Example:
import React, { useReducer } from 'react';
type State = { count: number };
type Action = { type: 'increment' } | { type: 'decrement' };
const reducer = (state: State, action: Action): State => {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error('Unknown action type');
}
};
const Counter: React.FC = () => {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
</div>
);
};
export default Counter;
Explanation:
-
useReducertakes a reducer function and an initial state, and returns the current state and adispatchfunction to trigger actions. - The reducer function determines how the state should be updated based on the action type.
5. useRef
The useRef hook is used to create a mutable reference that persists across renders. It is commonly used to access DOM elements or store mutable values without triggering re-renders.
Syntax:
const ref = useRef<Type>(initialValue);
Example:
import React, { useRef, useEffect } from 'react';
const FocusInput: React.FC = () => {
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (inputRef.current) {
inputRef.current.focus();
}
}, []);
return <input ref={inputRef} type="text" />;
};
export default FocusInput;
Explanation:
-
useRefreturns a mutable object with acurrentproperty. - Changes to
ref.currentdo not trigger re-renders.
6. useMemo
The useMemo hook is used to memoize expensive calculations so that they are only recomputed when their dependencies change.
Syntax:
const memoizedValue = useMemo<Type>(() => computeExpensiveValue(a, b), [a, b]);
Example:
import React, { useMemo, useState } from 'react';
const ExpensiveComponent: React.FC = () => {
const [count, setCount] = useState<number>(0);
const [input, setInput] = useState<string>('');
const squaredValue = useMemo(() => {
console.log('Calculating squared value...');
return count * count;
}, [count]);
return (
<div>
<p>Count: {count}</p>
<p>Squared Value: {squaredValue}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<input value={input} onChange={(e) => setInput(e.target.value)} />
</div>
);
};
export default ExpensiveComponent;
Explanation:
-
useMemotakes a function and an array of dependencies. It returns the memoized value, which is only recalculated when the dependencies change.
7. useCallback
The useCallback hook is used to memoize functions so that they are only recreated when their dependencies change. This is useful for optimizing performance in child components that rely on reference equality.
Syntax:
const memoizedCallback = useCallback<Function>(() => {
// Function logic
}, [dependencies]);
Example:
import React, { useState, useCallback } from 'react';
const ChildComponent: React.FC<{ onClick: () => void }> = ({ onClick }) => {
console.log('ChildComponent rendered');
return <button onClick={onClick}>Click Me</button>;
};
const ParentComponent: React.FC = () => {
const [count, setCount] = useState<number>(0);
const handleClick = useCallback(() => {
setCount((prev) => prev + 1);
}, []);
return (
<div>
<p>Count: {count}</p>
<ChildComponent onClick={handleClick} />
</div>
);
};
export default ParentComponent;
Explanation:
-
useCallbackreturns a memoized version of the function that only changes if one of the dependencies has changed.
8. useLayoutEffect
The useLayoutEffect hook is similar to useEffect, but it runs synchronously after all DOM mutations. Use this hook when you need to read layout from the DOM and re-render synchronously.
Syntax:
useLayoutEffect(() => {
// Synchronous logic
}, [dependencies]);
Example:
import React, { useLayoutEffect, useRef } from 'react';
const MeasureElement: React.FC = () => {
const ref = useRef<HTMLDivElement>(null);
useLayoutEffect(() => {
if (ref.current) {
console.log('Element width:', ref.current.offsetWidth);
}
}, []);
return <div ref={ref}>Measure Me</div>;
};
export default MeasureElement;
Explanation:
-
useLayoutEffectis useful for DOM measurements or when you need to perform actions that depend on the DOM layout.
9. useImperativeHandle
The useImperativeHandle hook customizes the instance value that is exposed when using ref with forwardRef.
Syntax:
useImperativeHandle(ref, () => ({
// Custom instance methods
}));
Example:
import React, { useRef, useImperativeHandle, forwardRef } from 'react';
type InputRef = {
focus: () => void;
};
const CustomInput = forwardRef<InputRef>((props, ref) => {
const inputRef = useRef<HTMLInputElement>(null);
useImperativeHandle(ref, () => ({
focus: () => {
if (inputRef.current) {
inputRef.current.focus();
}
},
}));
return <input ref={inputRef} type="text" />;
});
const ParentComponent: React.FC = () => {
const inputRef = useRef<InputRef>(null);
return (
<div>
<CustomInput ref={inputRef} />
<button onClick={() => inputRef.current?.focus()}>Focus Input</button>
</div>
);
};
export default ParentComponent;
Explanation:
-
useImperativeHandleis used to expose specific methods or properties to parent components.
10. useDebugValue
The useDebugValue hook is used to display a label for custom hooks in React DevTools.
Syntax:
useDebugValue(value);
Example:
import { useState, useDebugValue } from 'react';
const useCustomHook = () => {
const [value] = useState<string>('Hello');
useDebugValue(value);
return value;
};
const DebugValueExample: React.FC = () => {
const value = useCustomHook();
return <p>{value}</p>;
};
export default DebugValueExample;
Explanation:
-
useDebugValueis primarily used for debugging custom hooks.
Conclusion
React hooks provide a powerful and flexible way to manage state, side effects, and other features in functional components. By understanding and using these hooks effectively, you can write cleaner, more maintainable, and performant React applications. With TypeScript, you can add type safety to your hooks, making your code more robust and easier to debug.
Whether you're managing state with useState, handling side effects with useEffect, or optimizing performance with useMemo and useCallback, hooks are an essential tool in every React developer's toolkit. Happy coding!
Top comments (0)