DEV Community

Cover image for React Hooks: A Comprehensive Guide to Built-in React Hooks
Ryosuke Yano
Ryosuke Yano

Posted on

React Hooks: A Comprehensive Guide to Built-in React Hooks

Introduction

I've been using React for about two years, so I thought I'd review Hooks once here, so I wrote an article on how to use the basics of Hooks. React Hooks have revolutionized the way developers build dynamic components in React applications. With Hooks, managing state, sharing data, handling effects, and enhancing performance has become more intuitive and efficient. In this all-inclusive guide, we will delve into the world of React Hooks, exploring essential ones like useState, useReducer, useContext, useRef, useEffect, useMemo, and useCallback, to unleash their full potential in your projects.

1. State Hooks

  • useState

The useState Hook enables functional components to manage state effortlessly, allowing easy initialization and updates of state variables.

import React, { useState } from 'react';

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

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode
  • useReducer

The useReducer Hooks is ideal for managing complex state logic, utilizing a reducer function and providing a more advanced alternative to useState.

import React, { useReducer } from 'react';

const initialState = { count: 0 };

const reducer = (state, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    default:
      return state;
  }
};

const Counter = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

2. Context Hooks

  • useContext

The useContext Hook provides seamless access to data and functions from a React context, eliminating the need for multiple context consumers.

import React, { useContext } from 'react';

const UserContext = React.createContext();

const App = () => {
  const user = { name: 'John', age: 30 };
  return (
    <UserContext.Provider value={user}>
      <UserProfile />
    </UserContext.Provider>
  );
};


const UserProfile = () => {
  const user = useContext(UserContext);
  return <p>Name: {user.name}, Age: {user.age}</p>;
};
Enter fullscreen mode Exit fullscreen mode

3. Ref Hooks

  • useRef

The useRef Hook facilitates handling references to DOM elements or values that persist across renders, helping to maintain data between function calls.

import React, { useRef } from 'react';

const TextInput = () => {
  const inputRef = useRef(null); // returns { current: null }

  const handleFocus = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={handleFocus}>Focus Input</button>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode
  • useImperativeHandle (This is rarely used)

The useImperativeHandle Hook allows customization of the instance value that is exposed to parent components when using the ref prop.

import React, { useRef, useImperativeHandle, forwardRef } from 'react';

const CustomInput = forwardRef((props, ref) => {
  const inputRef = useRef();

  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    },
  }));

  return <input ref={inputRef} type="text" />;
});

  const ParentComponent = () => {
  const customInputRef = useRef();

  const handleFocus = () => {
    customInputRef.current.focus();
  };

  return (
    <div>
      <CustomInput ref={customInputRef} />
      <button onClick={handleFocus}>Focus Custom Input</button>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

4. Effect Hooks

  • useEffect

The useEffect Hook handles side effects like data fetching subscriptions, and DOM manipulation, seamlessly integrating with functional components.

import React, { useEffect, useState } from 'react';

const DataFetching = () => {
  const [data, setData] = useState([]);

  useEffect(() => {
    fetch('https://api.example.com/data')
      .then((response) => response.json())
      .then((data) => setData(data));
  }, []);

  return (
    <div>
      {data.map((item) => (
        <p key={item.id}>{item.name}</p>
      ))}
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode
  • useLayoutEffect

The useLayoutEffect Hook is similar to useEffect, but it runs synchronously after all DOM mutations, ensuring immediate updates.

import React, { useState, useLayoutEffect } from 'react';

const ResizeObserver = () => {
  const [width, setWidth] = useState(0);

  useLayoutEffect(() => {
    const handleResize = () => {
      setWidth(window.innerWidth);
    };

    window.addEventListener('resize', handleResize);
    handleResize();

    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return <p>Window Width: {width}</p>;
};
Enter fullscreen mode Exit fullscreen mode
  • useInsertionEffect (For CSS-in-JS library authors)

The useInsertionEffect Hook is used for styling transitions during the insertion of new elements into the DOM.

import React, { useState, useInsertionEffect } from 'react';

const List = () => {
  const [items, setItems] = useState([]);

  useInsertionEffect(() => {
    // Add transition styles for new items
  });

  const handleAddItem = () => {
    setItems([...items, { id: items.length + 1, name: `Item ${items.length + 1}` }]);
  };

  return (
    <div>
      <button onClick={handleAddItem}>Add Item</button>
      <ul>
        {items.map((item) => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

5. Performance Hooks

  • useMemo

The useMemo Hook efficiently memorizes the result of a function, preventing expensive recalculations and optimizing rendering performance.

import React, { useMemo, useState } from 'react';

const Fibonacci = ({ n }) => {
  const fib = useMemo(() => {
    if (n <= 1) return n;
    return Fibonacci({ n: n - 1 }) + Fibonacci({ n: n - 2 });
  }, [n]);

  return <p>Fibonacci({n}): {fib}</p>;
};
Enter fullscreen mode Exit fullscreen mode
  • useCallback

The useCallback Hook memorizes a callback function, preventing unnecessary recreations and optimizing performance in child components.

import React, { useCallback, useState } from 'react';

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

  const increment = useCallback(() => {
    setCount((prevCount) => prevCount + 1);
  }, []);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode
  • useTransition

The useTransition Hook allows concurrent rendering of multiple components, enhancing performance for complex user interfaces.

import React, { useTransition, useState } from 'react';

const TransitionExample = () => {
  const [isPending, startTransition] = useTransition({ timeoutMs: 500 });

  const handleButtonClick = () => {
    startTransition(() => {
      // Perform state updates or fetch data
    });
  };

  return (
    <div>
      <button disabled={isPending} onClick={handleButtonClick}>
        {isPending ? 'Loading...' : 'Start Transition'}
      </button>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode
  • useDeferredValue

The useDeferredValue Hook defers the rendering of a value, optimizing performance for asynchronous updates.

import React, { useDeferredValue, useState } from 'react';

const AsyncValue = () => {
  const [isPending, setIsPending] = useState(true);
  const deferredValue = useDeferredValue(isPending);

  // Simulate async data fetching
  setTimeout(() => {
    setIsPending(false);
  }, 2000);

  return <p>{deferredValue ? 'Fetching data...' : 'Data loaded'}</p>;
};
Enter fullscreen mode Exit fullscreen mode

6. Other Hooks

  • useDebugValue

The useDebugValue Hook enables custom labeling of custom hooks in React DevTools for easier debugging.

import { useDebugValue, useState } from 'react';

const useCustomHook = () => {
  const [count, setCount] = useState(0);
  useDebugValue(`CustomHook: ${count}`);
  return [count, setCount];
};
Enter fullscreen mode Exit fullscreen mode
  • useId

The useId Hook generates unique IDs that persist across renders, useful for labeling from elements or other components.

import { useId } from 'react';

const UniqueComponent = () => {
  const uniqueId = useId();

  return <p>{`Element ID: ${uniqueId}`}</p>;
};
Enter fullscreen mode Exit fullscreen mode
  • useSyncExternalStore

The useSyncExternalStore Hook enables synchronization between local and external data stores for maintaining consistent data flow.

import { useSyncExternalStore } from 'react';

const DataSync = () => {
  const [localData, setLocalData] = useState([]);
  useSyncExternalStore('https://api.example.com/data', setLocalData);

  return (
    <div>
      {localData.map((item) => (
        <p key={item.id}>{item.name}</p>
      ))}
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Conclusion

React Hooks offers a wide array of powerful tools for state management, context handling, effect management, and performance optimization. By leveraging the versatility of these hooks, developers can create efficient and maintainable React applications that deliver seamless user experiences. Remember to explore the official React documentation and community resources to discover even more Hooks and enhance your React development skills. Happy coding with React!

Top comments (0)