React Hooks have revolutionized how we write React components. In this article, we’ll explore basic hooks and how to apply them in real-world projects.

Basic Hooks

1. useState Hook

useState is the most basic hook, helping manage state in 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>
  );
}

2. useEffect Hook

useEffect replaces lifecycle methods in class components:

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

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds((seconds) => seconds + 1);
    }, 1000);

    return () => clearInterval(interval);
  }, []);

  return <div>Timer: {seconds}s</div>;
}

Advanced Hooks

1. useContext Hook

useContext helps consume Context values easily:

import React, { useContext } from "react";
import { ThemeContext } from "./ThemeContext";

function ThemedButton() {
  const theme = useContext(ThemeContext);

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

2. useReducer Hook

useReducer is useful for complex state management:

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 (
    <div>
      Count: {state.count}
      <button onClick={() => dispatch({ type: "increment" })}>+</button>
      <button onClick={() => dispatch({ type: "decrement" })}>-</button>
    </div>
  );
}

Best Practices

1. Custom Hooks

Create reusable logic with custom hooks:

import { useState, useEffect } from "react";

function useLocalStorage(key, initialValue) {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      return initialValue;
    }
  });

  const setValue = (value) => {
    try {
      setStoredValue(value);
      window.localStorage.setItem(key, JSON.stringify(value));
    } catch (error) {
      console.error(error);
    }
  };

  return [storedValue, setValue];
}

2. Performance Optimization

Use useMemo and useCallback for performance:

import React, { useMemo, useCallback } from "react";

function ExpensiveComponent({ items, onItemClick }) {
  const expensiveValue = useMemo(() => {
    return items.reduce((sum, item) => sum + item.value, 0);
  }, [items]);

  const handleClick = useCallback(
    (id) => {
      onItemClick(id);
    },
    [onItemClick],
  );

  return (
    <div>
      <p>Total: {expensiveValue}</p>
      {items.map((item) => (
        <button key={item.id} onClick={() => handleClick(item.id)}>
          {item.name}
        </button>
      ))}
    </div>
  );
}

Conclusion

React Hooks provide a powerful and flexible way to manage state and side effects in functional components. By mastering these hooks, you can write cleaner, more reusable code that’s easier to test and maintain.

Remember to:

  • Use the right hook for your use case
  • Follow the Rules of Hooks
  • Create custom hooks for reusable logic
  • Optimize performance when necessary

Happy coding with React Hooks! 🚀