DEV Community

Cover image for Advanced Hooks in React: Beyond useState and useEffect
Shlomi Sela
Shlomi Sela

Posted on • Edited on • Originally published at blog.shlomisela.com

Advanced Hooks in React: Beyond useState and useEffect

Exploring advanced React hooks is key for developers looking to create efficient and scalable applications. This article dives into these hooks, offering insights and practical code examples.

Introduction

React's Hooks API revolutionized functional components by providing a more intuitive way to handle state and side effects. Beyond the basics, advanced hooks offer nuanced control and optimization capabilities.

useReducer

useReducer is ideal for complex state logic, offering a more structured approach than useState.

Example:

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, { count: 0 });
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

useCallback

useCallback is critical for preventing unnecessary re-renders, especially with memoized components.

Example and Pitfall:

const MyComponent = React.memo(({ onClick }) => {
  // Component implementation
});

function ParentComponent() {
  const [value, setValue] = useState('');

  // Incorrect use of useCallback can lead to unnecessary re-renders
  const handleClick = useCallback(() => {
    console.log('Value:', value);
  }, []); // Missing dependency: value

  return <MyComponent onClick={handleClick} />;
}
Enter fullscreen mode Exit fullscreen mode

In this example, MyComponent will re-render whenever value changes because handleClick is not correctly memoized due to a missing dependency.

useMemo

useMemo memoizes expensive calculations to optimize performance.

Example:

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
Enter fullscreen mode Exit fullscreen mode

useRef

useRef is used for persisting values across renders and accessing DOM elements.

Example:

const inputEl = useRef(null);
const focusInput = () => inputEl.current && inputEl.current.focus();
Enter fullscreen mode Exit fullscreen mode

useContext

useContext simplifies state management across components, making it easier to share data.

Example:

const value = useContext(MyContext);

// Example of a context provider
const MyContextProvider = ({ children }) => {
  const [value, setValue] = useState(initialValue);
  return <MyContext.Provider value={{ value, setValue }}>{children}</MyContext.Provider>;
}
Enter fullscreen mode Exit fullscreen mode

Creating Custom Hooks

Custom hooks encapsulate logic and promote reusability.

Example: useFetch Hook:

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

  useEffect(() => {
    fetch(url)
      .then(response => response.json())
      .then(data => {
        setData(data);
        setLoading(false);
      });
  }, [url]);

  return { data, loading };
}
Enter fullscreen mode Exit fullscreen mode

(You can use any method instead of fetch.)

Conclusion

Mastering advanced hooks in React is crucial for creating efficient, clean, and maintainable applications.

Further Resources

Top comments (0)