Introduction - What is a React Hook?
A React Hook is a special function that lets you "hook into" React features like state and lifecycle methods, enabling functional components to manage state and side effects.
Now, you've decided to build a React app, leveraging the power of React hooks. With numerous hooks available, it can be challenging to know which ones to use.
Let's explore the essential hooks, understand their functions, and see some concise examples to get you started:
State Management Hooks
useState
The useState
hook is fundamental for managing state and rendering components upon state changes.
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
useReducer
The useReducer
hook is ideal for handling more complex state logic.
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: return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>{state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
</div>
);
}
Effect Hooks
useEffect
Effect hooks allow you to perform side effects, such as data fetching or DOM manipulation.
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
useLayoutEffect
useLayoutEffect
runs synchronously after all DOM mutations, useful for operations that need to occur before the browser repaints.
import React, { useLayoutEffect, useRef } from 'react';
function LayoutEffectExample() {
const ref = useRef();
useLayoutEffect(() => {
console.log(ref.current.getBoundingClientRect());
}, []);
return <div ref={ref}>Measure my dimensions</div>;
}
Ref Hooks
useRef
The useRef
hook allows you to persist values across renders without causing a re-render.
import React, { useRef } 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>
);
}
useImperativeHandle
useImperativeHandle
customizes the instance value that is exposed when using ref
in parent components.
import React, { useImperativeHandle, useRef, forwardRef } from 'react';
const FancyInput = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} />;
});
function Parent() {
const inputRef = useRef();
return (
<div>
<FancyInput ref={inputRef} />
<button onClick={() => inputRef.current.focus()}>Focus the input</button>
</div>
);
}
Performance Hooks
useMemo
useMemo
memoizes expensive calculations, recomputing the cached value only when its dependencies change.
import React, { useMemo } from 'react';
function ExpensiveCalculationComponent({ num }) {
const result = useMemo(() => num * 2, [num]);
return <div>Result: {result}</div>;
}
useCallback
useCallback
memoizes callback functions, preventing unnecessary re-creations on each render.
import React, { useState, useCallback } from 'react';
function Button({ onClick }) {
return <button onClick={onClick}>Click me</button>;
}
function App() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => setCount(count + 1), [count]);
return (
<div>
<p>{count}</p>
<Button onClick={handleClick} />
</div>
);
}
Context Hooks
useContext
useContext
accesses values from a context provider.
import React, { useContext } from 'react';
const MyContext = React.createContext();
function Display() {
const value = useContext(MyContext);
return <div>{value}</div>;
}
function App() {
return (
<MyContext.Provider value="Hello, World!">
<Display />
</MyContext.Provider>
);
}
Transition Hooks
useTransition
useTransition
marks state updates as non-urgent, improving user experience by deferring heavy computations.
import React, { useState, useTransition } from 'react';
function FilterList({ items }) {
const [filter, setFilter] = useState('');
const [isPending, startTransition] = useTransition();
const filteredItems = items.filter((item) =>
item.toLowerCase().includes(filter.toLowerCase())
);
return (
<div>
<input
type="text"
value={filter}
onChange={(e) => {
startTransition(() => setFilter(e.target.value));
}}
/>
{isPending ? <p>Loading...</p> : filteredItems.map((item) => <p key={item}>{item}</p>)}
</div>
);
}
useDeferredValue
useDeferredValue
defers a value update to maintain responsiveness.
import React, { useState, useDeferredValue } from 'react';
function DeferredList({ items }) {
const [filter, setFilter] = useState('');
const deferredFilter = useDeferredValue(filter);
const filteredItems = items.filter((item) =>
item.toLowerCase().includes(deferredFilter.toLowerCase())
);
return (
<div>
<input type="text" value={filter} onChange={(e) => setFilter(e.target.value)} />
{filteredItems.map((item) => (
<p key={item}>{item}</p>
))}
</div>
);
}
Conclusion
React hooks provide a powerful and flexible way to manage state, handle side effects, and optimize performance in your React applications. By understanding and utilizing these essential hooks, you can build more efficient and maintainable React apps. For a deeper dive into all things React, consider exploring comprehensive resources and bootcamps that offer interactive challenges, videos, and cheat sheets.