• FrontendJoy
  • Posts
  • 17 React Hooks That Power 90% of Modern Components

17 React Hooks That Power 90% of Modern Components

In partnership with

New to React?

If yes, you’ve probably wondered what hooks you really need—or worse, you’ve been copying the same logic over and over without realizing it.

This post covers all the React hooks you’ll likely need in your project.

Let’s dive in.

1. useState

This hook is unavoidable.

Whenever you need dynamic state that persists across re-renders, this is the one.

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

2. useReducer

Once your state becomes complex (e.g. multiple correlated fields), useReducer becomes your friend.

It also pairs really well with useContext, letting you update state with simple dispatch calls.

const reducer = (state, action) => {
  switch (action.type) {
    case "addTodo":
      return { todos: [...state.todos, action.todo] };
    case "removeTodo":
      // TODO: Implement logic
    case "toggleTodo":
      // TODO: Implement logic
    default:
      return state;
  }
};

const [state, dispatch] = useReducer(reducer, { todos: [] });

3. useContext

When prop drilling gets out of hand, this hook saves the day.

Prop drilling = passing props through multiple layers just to reach a deeply nested component.

Use useContext to:

  • manage global state in small apps

  • share data like the authenticated user

  • avoid passing props manually everywhere

const UserContext = createContext();

function App() {
  const user = useUser();
  return (
    <UserContext.Provider value={user}>
      {/* ... */}
    </UserContext.Provider>
  );
}

function Profile() {
  const user = useContext(UserContext);
  return <div>Hello {user.name}</div>;
}

4. useEffect

You should avoid it 99% of the time.
(See: You Might Not Need an Effect)

But in real life, you’ll still need it—especially for things like syncing with external systems.

function useSetDocumentTitle(title: string) {
  useEffect(() => {
    document.title = title;
  }, [title]);
}

5. useRef

You should never directly manipulate the DOM in React.

But when you must (e.g. for canvas, chart libraries…), useRef is the safe way.

function ExampleChart({ data }) {
  const canvasRef = useRef();

  useEffect(() => {
    const ctx = canvasRef.current;
    if (!ctx) return;

    const chart = new Chart(ctx, {
      type: "bar",
      data: {
        labels: data.map((row) => row.year),
        datasets: [{ label: "Likes", data: data.map((r) => r.count) }],
      },
    });

    return () => chart.destroy();
  }, []);

  return <canvas ref={canvasRef} />;
}

6. useMemo / useCallback

These may become optional thanks to the new React Compiler—but for now, they’re still useful.

Use useMemo when:

  • an expensive computation should not re-run on every render

  • you're memoizing a non-primitive dependency

  • you're passing values to memo-wrapped components

Use useCallback to memoize functions.

const total = useMemo(() => computeTotal(items), [items]);

const handleClick = useCallback(() => {
  console.log("clicked");
}, []);

7. useLayoutEffect

This one is rare but useful.

It runs before paint, which helps prevent layout shifts or flickering UI.

See real examples here: No more flickering UI

If you’re not comfortable with React components lifecycle, check my post React Lifecycle in 3 Minutes 😉.

useLayoutEffect(() => {
  const height = ref.current.offsetHeight;
  setHeight(height);
}, []);

8. useSWR / useQuery

SWR and TanStack Query are becoming the standard for fetching data in React.

SWR is simpler and great for most use cases while TanStack Query is more complex and powerful.

SWR

const fetcher = (url) => fetch(url).then((res) => res.json());

const { data, error, isLoading } = useSWR("/api/user", fetcher);

TanStack Query

const getUser = () => fetch("/api/user").then((res) => res.json());

const { data, isLoading } = useQuery({
  queryKey: ["user"],
  queryFn: getUser,
});

9. useSelector, useDispatch, or other state hooks

Sometimes React’s built-in state tools aren’t enough.

You’ll then see these hooks depending on the state library:

10. useNavigate

React Router is very popular.

So is its navigation hook:

const navigate = useNavigate();
navigate("/profile");

Now, let’s talk about hooks I reach for in nearly every app—even though they’re not built into React.

11. useBoolean

One of my all-time favorites.

It makes managing boolean state super clean.

const { value: isOpen, setFalse, toggle } = useBoolean();

return (
  <>
    <button onClick={toggle}>
      {isOpen ? "Close form" : "Open form"}
    </button>
    <Dialog isOpen={isOpen} onClose={setFalse}>
      {/* Form content */}
    </Dialog>
  </>
);

12. useCopyToClipboard

Perfect for "copy" buttons.

const [copiedText, copy] = useCopyToClipboard();

const handleCopy = () => {
  copy("Hello World").catch(err => {
    errorToast("Failed to copy!", err);
  });
};

return (
  <>
    <button onClick={handleCopy}>Copy</button>
    {copiedText && <span>Copied!</span>}
  </>
);

13. usePrevious

Use this to compare current vs. previous values.

Great for triggering actions based on changes.

const prevCount = usePrevious(count);
const didIncrease = prevCount !== undefined && count > prevCount;

useEffect(() => {
  if (didIncrease) {
    console.log("Count increased!");
  }
}, [didIncrease]);

14. useDebounce

Debounce your requests to avoid unnecessary API calls.

const [query, setQuery] = useState("");
const [debouncedQuery] = useDebounce(query, 500);

const { data } = useSWR(`/api/search?q=${debouncedQuery}`, fetcher);

return (
  <> 
    <input
      value={query}
      onChange={(e) => setQuery(e.target.value)}
      placeholder="Search..."
    />
    {/* TODO: Show data */}
  </>
  
);

15. useMediaQuery

Want to render different things based on screen size? This hook is for you.

const isMobile = useMediaQuery("(max-width: 768px)");

return <div>{isMobile ? "Mobile View" : "Desktop View"}</div>;

16. useResizeObserver

Useful for responsive layouts or libraries like react-window.

const ref = useRef(null);
const { width = 0, height = 0 } = useResizeObserver({ ref });

return (
  <div ref={ref}>
    Width: {width}, Height: {height}
  </div>
);

17. useLocalStorage

Store and sync data with localStorage easily.

const [theme, setTheme] = useLocalStorage("theme", "light");

return (
  <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
    Switch to {theme === "light" ? "dark" : "light"}
  </button>
);

Summary

That’s it!

These 17 hooks are more than enough to cover most use cases in any React app.

Pick your favorites and use them in your next project 😉.

💡 TIP OF THE WEEK

Reply

or to participate.