Blog post

Guide to React Hooks: Learn how to use every built-in React Hook effectively to enhance your functional components and improve your React projects.

blog post cover

Complete Guide to React Hooks

React Hooks revolutionized the way we write React components, allowing developers to use state and other React features in functional components. Hooks provide a more direct API to the React concepts we know and love—state, context, refs, and more. In this post, we'll explore every built-in React hook and how to use them effectively in your React projects.

Core Hooks

useState

useState is the most commonly used hook. It allows you to add state to functional components.

import React, { useState } from 'react';

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

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}

Here, useState initializes the state with 0. setCount is a function that updates the state.

useEffect

useEffect allows you to perform side effects in functional components. It runs after every render by default.

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

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

  useEffect(() => {
    const timer = setInterval(() => {
      setCount((prevCount) => prevCount + 1);
    }, 1000);

    return () => clearInterval(timer); // Cleanup on unmount
  }, []); // Dependency array

  return <h1>{count}</h1>;
}

In this example, useEffect sets up a timer and cleans it up when the component unmounts.

useContext

useContext is used to access the value of a React context. It's a simpler way to consume context values without using the <Context.Consumer> component.

import React, { useContext } from 'react';

const UserContext = React.createContext();

function UserProfile() {
  const user = useContext(UserContext);

  return <div>Hello, {user.name}!</div>;
}

useContext takes a context object and returns the current context value.

useReducer

useReducer is useful for managing complex state logic that involves multiple sub-values or when the next state depends on the previous state.

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:
      throw new Error();
  }
}

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

  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </>
  );
}

useReducer takes a reducer function and an initial state and returns the current state and a dispatch function.

useRef

useRef provides a way to persist values between renders without causing a re-render when updated. It is also commonly used to access DOM elements.

import React, { useRef } from 'react';

function TextInput() {
  const inputRef = useRef(null);

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

  return (
    <>
      <input ref={inputRef} type="text" />
      <button onClick={focusInput}>Focus Input</button>
    </>
  );
}

useRef creates a reference to a DOM element or a mutable value that persists for the full lifetime of the component.

Additional Hooks

useMemo

useMemo memoizes the result of a calculation and recomputes it only when one of its dependencies has changed. It's useful for performance optimization.

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

function ExpensiveCalculationComponent({ num }) {
  const [multiplier, setMultiplier] = useState(1);

  const result = useMemo(() => {
    console.log('Calculating...');
    return num * multiplier;
  }, [num, multiplier]);

  return (
    <div>
      <h1>{result}</h1>
      <button onClick={() => setMultiplier(multiplier + 1)}>Increase Multiplier</button>
    </div>
  );
}

useCallback

useCallback is similar to useMemo but is used to memoize functions. It returns a memoized version of a callback function that only changes if one of its dependencies has changed.

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

function Button({ onClick }) {
  console.log('Button re-rendered');
  return <button onClick={onClick}>Click me</button>;
}

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

  const handleClick = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  return (
    <div>
      <Button onClick={handleClick} />
      <p>Count: {count}</p>
    </div>
  );
}

useLayoutEffect

useLayoutEffect is similar to useEffect but fires synchronously after all DOM mutations. It’s useful when you need to read layout from the DOM and re-render synchronously.

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

function LayoutEffectExample() {
  const divRef = useRef();

  useLayoutEffect(() => {
    console.log('Height:', divRef.current.clientHeight);
  });

  return <div ref={divRef}>Check the console for height</div>;
}

useImperativeHandle

useImperativeHandle customizes the instance value that is exposed when using ref. It's often used with forwardRef.

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

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

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

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

function ParentComponent() {
  const inputRef = useRef();

  return (
    <>
      <FancyInput ref={inputRef} />
      <button onClick={() => inputRef.current.focus()}>Focus Input</button>
    </>
  );
}

useDebugValue

useDebugValue can be used to display a label for custom hooks in React DevTools.

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

// Custom hook to manage user online status
function useOnlineStatus() {
  const [isOnline, setIsOnline] = useState(navigator.onLine);

  useDebugValue(isOnline ? 'Online' : 'Offline');

  React.useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }
    function handleOffline() {
      setIsOnline(false);
    }

    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  return isOnline;
}

// Component using the custom hook
function StatusIndicator() {
  const isOnline = useOnlineStatus();

  return <span>{isOnline ? '🟢 Online' : '🔴 Offline'}</span>;
}

export default StatusIndicator;

Custom Hooks

Creating custom hooks allows you to extract and reuse component logic. Here’s a basic example of a custom hook for data fetching:

import { useState, useEffect } from 'react';

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function fetchData() {
      const response = await fetch(url);
      const data = await response.json();
      setData(data);
      setLoading(false);
    }
    fetchData();
  }, [url]);

  return { data, loading };
}

Conclusion

React Hooks provide a powerful and flexible way to build modern React applications using functional components. Mastering these hooks can help you write cleaner, more maintainable code. Start using these hooks today to enhance your React development experience!

Written By

Logo

Published on:

9/12/2024, 8:31:11 PM

Share Post