DEV Community

Cover image for Explaining all React Hooks with examples
Sérgio Junior
Sérgio Junior

Posted on


Explaining all React Hooks with examples

React Hooks were released two years ago on React version 16.8. It's not so common to see the usage of all of them but they might be very useful to simplify a feature or improve the performance in our application, so, I'd like to explain and give some examples of usage of all React Hooks.

In this post I'll cover all React Hooks:

  • useState.
  • useEffect.
  • useRef.
  • useContext.
  • useReducer.
  • useMemo.
  • useCallback.
  • useDebug.
  • useLayoutEffect.


The simplest and most used of React Hooks, useState allows you to store any value in a function component, like string, object, array, boolean, number, null.

It has the same functionality of this.state in a React Class Component.

You can define its value by passing a value directly or calling a method that accesses the previous value and returns the new value.

import { useState } from "react";

export default function Component() {
  const [counter, setCounter] = useState(0);
  return (
      <p>Clicks: {counter}</p>
      <button onClick={() => setCounter((prev) => ++prev)}>Increase 1</button>
Enter fullscreen mode Exit fullscreen mode


useEffect allows you to call a method during some moments in the component lifecycle:

  • On the first render.
  • When a watched value is updated.
  • When the component is unmounted.

It's similar to the methods componentDidMount, componentDidUpdate and componentWillUnmount in a Class Component.

import { useEffect } from "react";

export default function Component() {
  const fetchSomething = (params) => {};
  const onResizeScreenHandler = () => {};

  useEffect(() => {
  }, [filters]);

  useEffect(() => {
    document.addEventlistener("resize", onResizeScreenHandler);
    return () => document.removeEventlistener("resize", onResizeScreenHandler);
  }, []);
Enter fullscreen mode Exit fullscreen mode

To call the function inside useEffect once, let the second parameter, an array, empty.

useEffect(fn, []);
Enter fullscreen mode Exit fullscreen mode

To observe a value, add it in the second parameter.

useEffect(() {
  // Do something with updated x
}, [x]);
Enter fullscreen mode Exit fullscreen mode

The function that you return will be called when the component is unmounted.

useEffect(() => {
  return () => doSomething();
}, []);
Enter fullscreen mode Exit fullscreen mode


The useRef hook lets you:

  • Access an element on the DOM.
  • Store an immutable value during the component's lifecycle.

Its value is accessed by .current:

const time = useRef(0);
console.log(time.current); // 0;
Enter fullscreen mode Exit fullscreen mode

Accessing an element on the DOM.

import { useRef, useEffect } from "react";

export default function Component() {
  const textRef = useRef(null);
  useEffect(() => {
    if (textRef) {
      const elementWidth = textRef.current.offsetWidth;
  }, [textRef]);

  return <p ref={textRef}>Hello, React Hooks</p>;
Enter fullscreen mode Exit fullscreen mode

Delay effect

You can use useRef to create a delay to call a method. For instance, wait x seconds to request something as soon the user finishes typing and not overload your API every time he types on deletes a letter.

import { useRef, useEffect, useRef, useState } from "react";

export default function Form() {
  const [text, setText] = useState("");
  const timeToCallSomething = useRef(null);

  const fetchSomething = () => {
    if (text) {
      // Fetch an API.

  useEffect(() => {
    if (timeToCallSomething.current) {
    timeToCallSomething.current = setTimeout(fetchSomething, 1000);
    return () => clearInterval(timeToCallSomething.current);
  }, [text]);

  const onChangeHandler = ({ target: { value } }) => {

  return <input value={text} onChange={onChangeHandler} />;
Enter fullscreen mode Exit fullscreen mode

Passing a ref by props

If you want to pass a ref to use in a child component, you need to use the method React.forwardRef in the child component and receive the ref as the second parameter.

import { useRef, useEffect, forwardRef } from "react";

export default function Parent() {
  const childRef = useRef(null);

  return <Text ref={childRef} />;

const Text = forwardRef((props, ref) => {
  return <p ref={ref}>Exemplo de texto</p>;
Enter fullscreen mode Exit fullscreen mode


It's an alternative to the useState hook and works like the reducer in Redux. It watches a pre-defined type and returns a new state depending on that type.

function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return state + 1;
    case "decrement":
      return state - 1;
      return state;

function Counter() {
  const [state, dispatch] = useReducer(reducer, 0);
  return (
      Count: {state}
      <button onClick={() => dispatch({ type: "decrement" })}>-</button>
      <button onClick={() => dispatch({ type: "increment" })}>+</button>
Enter fullscreen mode Exit fullscreen mode


The useContext hook allows you to store and access a value anywhere within its hierarchy. It works like Redux, by the way, I already wrote an article showing how to replace Redux by React Hooks.

It's useful when you often use a value and don't want to pass it to the children components by props.

// hooks/useUserContext.js
import { useContext, createContext } from "react";

// Our context
const UserContext = createContext({ name: "" });

// The main component that receives a value and updates our context.
export function UserContextProvider({ name, children }) {
  return <UserContext.Provider value={name}>{children}</UserContext.Provider>;

// A custom hook to get the current value.
export default function useUserContext() {
  return useContext(UserContext);
Enter fullscreen mode Exit fullscreen mode
// App.js
import { UserContextProvider } from "~hooks/useUserContext";

export default function App() {
  return (
    <UserContextProvider value="John">
      <YourAppTree />
Enter fullscreen mode Exit fullscreen mode
// YourAppTree.js
import useUserContext from "~hooks/useUserContext";

export default function YourAppTree() {
  const userName = useUserContext(); // Initially the value will be "" and then John.
  return <p>Hi, {userName}</p>;
Enter fullscreen mode Exit fullscreen mode

The next React Hooks, useMemo and useCallback, are commonly used to solve performance issues or improve them in our application.


You can use useMemo to save time and processing on the execution of a complex function.

Imagine that you have a function that receives some parameters and does count with them that's expensive to process. What the useMemo hook does is memorize these parameters and the returned value and whenever these parameters are equal again, it will return the previous value, without calculating it.

import { useMemo } from "react";

export default function Component() {
  const value = useMemo(() => {
    // Do something complex with x and y.
  }, [x, y]);
Enter fullscreen mode Exit fullscreen mode


Before explaining what the useCallback does, I'd like to explain an advanced and important concept in React.

React uses Strict Equality Comparison to compare the props or the observable values in a React Hook. If any of these values are different, it will update our component or call the method inside the React Hook.

And you should know like Patrick does that things aren't so obvious in javascript with non-primitive values.


Primitive values are string, number and boolean. Array, object and function aren't primitives, so, if you compare them, you'll get false:

1 === 1; // true;
"batman" === "batman"; // true;
false === false; // true;

{} === {}; // false;
[] === []; // false;
() => {} === () => {}; //false;
Enter fullscreen mode Exit fullscreen mode

It will only return true if it has the same reference, like a variable:

const fn = () => {};
fn === fn; // true;
Enter fullscreen mode Exit fullscreen mode

So if you pass a function by props, React will update the component every time, even if the props haven't changed, because a function is different from a function.

Therefore we use useCallback to create a unique reference to a function and React knows that it's the same and avoids unnecessary updates.

Its reference only will be different in case the parameters change.

import { useCallback } from "react";

export default function Parent() {
  const onFetchAlwaysHandler = useCallback(() => {
    // Its reference will change when a and b change.
  }, [a, b]);

  const onFetchOnceHandler = useCallback(() => {
    // It will have the same reference.
  }, []);

  return <Approvad onFetchHandler={onFetchHandler} />;
Enter fullscreen mode Exit fullscreen mode

Watch out

If you use a function with useCallback as a dependency of useEffect that updates the useCallback parameters, you can cause an infinite loop.

Improving our performance with React.memo

You can use useCallback and React.memo to improve the performance in your application and avoid that the children components update whenever that's an update on the parent component.

function List({ item, onSelect }) {
  return (
      <button onClick={onSelect}>Select {item}</button>

const MemorizedList = React.memo(List);

function App() {
  const onSelectHandler = useCallback((selectedItem) => {
    // Do something with selectedItem.
  }, []);

  return (
      { => (
        <MemorizedList item={e} onSelect={onSelectHandler} />
Enter fullscreen mode Exit fullscreen mode


The hook useLayoutEffect has the same functionality as useEffect, however, it will execute its function as soon the browser finishes mounting the dom.

You can use this hook to read an element in the DOM or do something when the page loads.

import { useState, useLayoutEffect } from "react";

function Component() {
  const [loadedDOM, setLoadedDOM] = useState(false);

  useLayoutEffect(() => {
  }, []);
Enter fullscreen mode Exit fullscreen mode


If you're using Server Side Render (NextJS), the React Hooks useLayoutEffect and useEffect won't work because they only run on the client-side. If you try, React will show you a warning on the console.

You can use them to interact with the localstorage which's a feature only in the client, for example.


The hook useDebugValue can be used to show a label on the React DevTools extension inside a custom hook. It works like a console.log.

import { useDebugValue, useState } from "react";

function useUserStatus() {
  const [isLogged, setIsLogged] = useState(false);


  useDebugValue(isLogged ? "Logged" : "Not logged");

  return isLogged;
Enter fullscreen mode Exit fullscreen mode


The useImperativeHandle hook allows you to customize the ref value in a parent component through its child component. You can pass a DOM element, method, anything and be able to call it from the parent component. It must be used with the React.forwardRef in the child component to receive the ref from the parent component.

import { useImperativeHandle, forwardRef, useEffect, useRef } from "react";

const Input = forwardRef((props, parentRef) => {
  const childRef = useRef();
  const someMethod = () => 1;

  useImperativeHandle(parentRef, () => ({
    focusOnChildInput: () => childRef.current.focus(),
    callChildMethod: someMethod,
  return <input ref={childRef} />;

const Form = () => {
  const ref = useRef();
  useEffect(() => {
    if (ref) {
      ref.current?.callChildMethod?.(); // 1
  }, []);

  return (
      <Input ref={ref} />
Enter fullscreen mode Exit fullscreen mode


That's all, if you want to see more details or read about these React Hooks, please, take a look at the official React documentation.

Top comments (0)

An Animated Guide to Node.js Event Loop

Node.js doesn’t stop from running other operations because of Libuv, a C++ library responsible for the event loop and asynchronously handling tasks such as network requests, DNS resolution, file system operations, data encryption, etc.

What happens under the hood when Node.js works on tasks such as database queries? We will explore it by following this piece of code step by step.