A guide to the top 10 React Hooks!" 🪝

Hooks provide a more concise and intuitive approach compared to class components, enabling developers to build robust and functional UIs with ease. In this article, we'll explore the top 10 Hooks in React, diving into their purpose, usage, and how they can level up your React development game.

1.useState - Managing Component State:

The useState Hook allows us to introduce state into functional components. We can declare and update state variables, providing a reactive experience. With this Hook, we eliminate the need for class components and utilize a simpler syntax. Example:


import React, { useState } from 'react';

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

  const increment = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
};

Explanation:

In this example, we use the useState Hook to add a count state variable to our Counter component. We access the count value and a setter function (setCount) to update the count state. Clicking the "Increment" button increases the count and triggers a re-render.

2.useEffect - Side Effects and Lifecycle:

The useEffect Hook allows us to perform side effects, such as data fetching, subscriptions, or DOM manipulations, within functional components. It replaces the lifecycle methods of class components. Example:


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

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

  useEffect(() => {
    const fetchData = async () => {
      const response = await fetch('https://api.example.com/data');
      const data = await response.json();
      setData(data);
    };

    fetchData();
  }, []);

  return <div>{data ? <p>Data: {data}</p> : <p>Loading...</p>}</div>;
};

Explanation:

In this example, we use the useEffect Hook to fetch data from an API and update the component's state. The effect runs once (due to the empty dependency array []) after the initial render. It sets the fetched data to the state, triggering a re-render and displaying the data once it's available.

3.useContext - Accessing Context:

The useContext Hook simplifies the consumption of React Context within functional components. It allows us to access context values and avoid prop drilling. Example:


import React, { useContext } from 'react';

const ThemeContext = React.createContext('light');

const ThemeButton = () => {
  const theme = useContext(ThemeContext);

  return <button style={{ background: theme }}>Themed Button</button>;
};

Explanation:

In this example, we create a ThemeContext using createContext. The ThemeButton component consumes the theme value from the context using the useContext Hook. The button's background color is set dynamically based on the theme value provided by a higher-level component.

4.useRef - Accessing Mutable Values:

The useRef Hook provides a way to store mutable values that persist across renders. It's useful for accessing DOM elements, tracking previous values, or creating mutable variables. Example:


import React, { useRef } from 'react';

const InputWithFocus = () => {
  const inputRef = useRef(null);

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

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

Explanation:

In this example, we use the useRef Hook to create a reference to the input element. When the "Focus Input" button is clicked, the focusInput function is invoked, which focuses the input element using the ref's current value.

5.useMemo - Memoizing Expensive Calculations:

The useMemo Hook allows us to memoize expensive calculations, preventing unnecessary re-computations. It optimizes performance by caching the result and returning it when the dependencies remain unchanged. Example:

Copy code
import React, { useMemo } from 'react';

const ExpensiveCalculation = () => {
  const result = useMemo(() => {
    // Expensive calculation
    return performCalculation();
  }, []);

  return <div>Result: {result}</div>;
};

Explanation:

In this example, we use the useMemo Hook to memoize the result of an expensive calculation. The calculation is only performed when the dependencies (in this case, an empty array []) change. The result is cached and reused in subsequent renders if the dependencies remain the same.

6.useCallback - Memoizing Callback Functions:

The useCallback Hook is useful for memoizing callback functions, optimizing performance by preventing unnecessary re-renders of child components. Example:


import React, { useCallback } from 'react';

const Button = ({ onClick, label }) => {
  return <button onClick={onClick}>{label}</button>;
};

const ParentComponent = () => {
  const handleClick = useCallback(() => {
    console.log('Button clicked!');
  }, []);

  return <Button onClick={handleClick} label="Click me" />;
};

Explanation:

In this example, we use the useCallback Hook to memoize the handleClick function. The function is only recreated if the dependencies (empty array []) change. This ensures that the Button component does not unnecessarily re-render, even if the ParentComponent re-renders.

7.useReducer - Managing Complex State:

The useReducer Hook provides an alternative approach to useState for managing complex state logic. It's useful when state transitions involve multiple sub-values or require additional logic. Example:


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

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>
  );
};

Explanation:

In this example, we use the useReducer Hook to manage the state of the count variable. The reducer function handles state transitions based on dispatched actions. The dispatch function is used to trigger state updates by passing the corresponding action.

8.useLayoutEffect - Synchronizing with DOM Updates:

The useLayoutEffect Hook is similar to useEffect, but it fires synchronously after all DOM mutations. It's useful for performing DOM measurements or applying imperative DOM modifications that require immediate feedback. Example:


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

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

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

    window.addEventListener('resize', handleResize);

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

  return <div>Window Width: {width}</div>;
};

Explanation:

In this example, we use the useLayoutEffect Hook to update the width state whenever the window is resized. The effect runs synchronously after DOM mutations, ensuring accurate measurements. We add and remove the event listener within the effect to clean up after the component unmounts.

9.useImperativeHandle - Customizing Exposed Component Instances:

The useImperativeHandle Hook allows us to customize the value exposed by a component's ref. It's useful for abstracting imperative actions and exposing them to parent components. Example:


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

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

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

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

const ParentComponent = () => {
  const inputRef = useRef(null);

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

  return (
    <div>
      <FancyInput ref={inputRef} />
      <button onClick={handleClick}>Focus Input</button>
    </div>
  );
};

Explanation:

In this example, we use the useImperativeHandle Hook to customize the value exposed by the FancyInput component's ref. We define a focus method that triggers the input element's focus. The ParentComponent uses the ref to access the customized functionality and focus the input when the button is clicked.

10.useCustomHook - Building Custom Hooks:

The useCustomHook pattern allows us to encapsulate reusable logic into custom hooks. It promotes code reusability and composability across components. Example:

Copy code
import { useState, useEffect } from 'react';

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

  useEffect(() => {
    const fetchData = async () => {
      const response = await fetch('https://api.example.com/data');
      const data = await response.json();
      setData(data);
    };

    fetchData();
  }, []);

  return data;
};

const ComponentUsingCustomHook = () => {
  const data = useCustomHook();

  return <div>{data ? <p>Data: {data}</p> : <p>Loading...</p>}</div>;
};

Explanation:

In this example, we create a custom hook called useCustomHook, which encapsulates data fetching logic. The hook returns the fetched data, which can be used in any component that consumes it. The ComponentUsingCustomHook showcases the usage of the custom hook, rendering data or a loading indicator based on the state.

Conclusion:

In this article, we explored the top 10 Hooks in React, along with practical examples and explanations. Armed with these Hooks, you can take your React development skills to new heights and build exceptional UIs with ease.

Keep Learning! Keep coding ✌️