in

What are React Hooks and Why do I need them? A Deep Dive for React Developers

As a long-time React developer and lead engineer at [Acme Corp], Hooks have been one of the most impactful additions to React in my career. In this comprehensive deep dive, I‘ll share my insights on what Hooks are, why they‘re beneficial, and some best practices I‘ve learned from building numerous production apps with them.

Whether you‘re new to React or a grizzled veteran like me, Hooks represent a monumental shift in how we build components. Their adoption has been swift, but many developers still face confusion on practical use cases and patterns.

My goal is for you to finish this guide with an expert-level grasp of Hooks fundamentals and applications. I aim to answer:

  • What problems do Hooks solve that classes can‘t?
  • When should I actually use Hooks in my own code?
  • How do features like useState, useEffect, and useContext work under the hood?
  • What Hooks best practices will help me avoid common mistakes?

So brew some fresh coffee and let‘s dive deep!

What Motivated React to Add Hooks?

Before we look at what Hooks are and how to use them, it‘s useful to understand the problems that motivated React to introduce them.

As a lead engineer at Acme Corp, I‘ve built some incredibly complex React applications over the years. And while our class components were very capable, a few pain points kept emerging:

1. Hierarchy and abstraction were overused

React‘s component model encourages breaking UIs into small reusable pieces. However, to share stateful logic between components with classes, you end up wrapping components in layers of abstraction like render props and higher order components.

This adds a lot of hierarchy when all I want to do is share some state or imperative logic between components!

2. Classes confuse React newcomers

Classes are familiar if you‘re coming from an OOP background. But for newcomers with more functional experience, key React concepts like lifecycle methods feel foreign. Hooks lower the barrier to entry.

3. Related logic is scattered in classes

With classes, you end up spreading related logic like data fetching across different lifecycle methods like componentDidMount and componentDidUpdate. Hooks let me colocate related imperative logic.

4. Classes encourage giant components

It‘s tempting to jam more logic into class lifecycle methods. Hooks encourage smaller functions that can be tested in isolation.

5. Classes do not minify as well as functions

Minified code size matters, especially on mobile. Functions minify better in bundles than classed.

Of course classes won‘t disappear from React overnight. But these pain points show why Hooks make sense as the primary way to write components going forward.

What Exactly Are React Hooks?

Alright, time for the main event. What are Hooks after all this hype?

Hooks in a Nutshell

Hooks are functions that let you "hook into" React features like state and lifecycle events. Their names always start with use like useState or useEffect.

For example, useState lets us add local state to a function component:

import { useState } from ‘react‘;

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

  return (
    // ...
  )
}

The useState Hook provides the local state variable and setter function.

There are a few built-in Hooks like useState, useEffect, useContext etc that cover common use cases. But you can also create custom Hooks to reuse stateful behavior.

Hooks don‘t replace your knowledge of React concepts. They just provide direct access to things like state in function components.

Why Are They Called "Hooks"?

The name "Hooks" refers to their ability to let you "hook into" React features like state and lifecycle events.

You invoke Hooks at the top level of function components to hook into and manage things like:

  • Local state via useState
  • Lifecycle events via useEffect
  • Context via useContext
  • And more…

Without Hooks, function components can only render UI based on their props. Hooks allow them to manage state and side effects too.

Hooks Are Backwards Compatible

A common concern is that adopting Hooks will require massive rewrites. Fortunately, that isn‘t the case!

Hooks work side-by-side with existing code so you can:

  • Adopt them gradually in new components
  • Keep using classes in older components without changes

In fact, React guarantees that Hooks won‘t break your existing class components.

This means you can safely try Hooks in new components now and migrate legacy ones over time.

Hooks Follow Essential Rules

There are a couple rules around using Hooks that are essential to follow:

1. Only call Hooks at the top level

Never call Hooks inside loops, conditions, or nested functions. This ensures the ordering of Hooks is consistent across renders.

2. Only call Hooks from React function components

Don‘t call Hooks from regular JavaScript functions. Following this ensures you‘re calling Hooks in components that follow React rules around updating.

As long as you stick to these two rules, you‘ll avoid the subtle bugs that come from mismatched Hook calls. The eslint-plugin-react-hooks plugin can help enforce them.

Now that we‘ve covered the Hooks basics, let‘s go over common use cases and examples.

When Should I Use React Hooks?

Hooks shine for certain use cases but aren‘t universally better than classes. Based on my experience, here are the scenarios where Hooks provide the most value:

1. When you need stateful logic in a function component

Before Hooks, if you wanted local state in a component you had to convert it to a class. Hooks let you add state and lifecycle logic to function components with useState and useEffect.

2. When you want to share stateful logic between components

With Hooks, it‘s easy to extract stateful logic like data fetching into reusable custom Hooks. Much cleaner than render props and HOCs!

3. When you need to access context in a function component

The useContext Hook provides direct access to React context without having to pass context as a prop through the tree.

4. When classes are getting hard to understand and maintain

If your classes have messy lifecycle logic, custom Hooks let you move that logic into isolated reusable functions.

In my experience, Hooks excel at tackling these specific problems compared to classes.

That said, there‘s no need to rush and rewrite all your class components overnight. Part of what makes Hooks powerful is the ability to add them incrementally alongside existing classes.

React Hooks By Example: Core Hooks in Depth

Now let‘s dive into practical examples of Hooks for common use cases:

useState for Local State

The useState Hook enables local state in function components. For example:

import { useState } from ‘react‘;

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

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

Calling useState declares a state variable count and a setter function setCount.

useState returns an array with 2 values:

  1. The current state value
  2. A setter function

It‘s a convention to name them like [value, setValue].

You can have multiple state variables by calling useState multiple times:

function MyComponent() {
  const [count, setCount] = useState(0);
  const [isOn, setIsOn] = useState(false);

  // ...
}

useState also lazily initializes state on first render avoiding expensive initialization computes.

Overall, useState makes local state in function components a breeze.

useEffect for Imperative Logic

The useEffect Hook enables running imperative logic after render like data fetching or subscriptions.

For example, we can log a message after render:

import { useEffect } from ‘react‘;

function MyComponent({ userId }) {
  useEffect(() => {
    console.log(‘Component rendered with user:‘, userId);
  });

  // ...
}

The function passed to useEffect runs after layout and paint. This is useful for work that needs to happen after the DOM has updated.

Effects also let you return a cleanup function that runs before the next execution:

useEffect(() => {
  // set up subscription

  return () => {
    // clean up subscription
  };
}, [dependencies]);

Specifying dependencies causes the effect to re-run only when those values change between renders. This handles side effects during mounting and updates.

Overall, useEffect provides a unified interface to express imperative logic in function components.

useContext for Consuming Context

The useContext Hook lets you subscribe to React context in function components:

const UserContext = React.createContext(null);

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

  return (
    <div>{user.name}</div>   
  ); 
}

Any component in the tree can access context with useContext without passing props down manually.

This avoids cumbersome context consumption patterns like context getters and render props.

useReducer for Complex State Logic

The useReducer Hook provides state management like Redux in function components.

It accepts a reducer function that manages application state and returns the current state value:

function reducer(state, action) {
  // return updated state
}

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

  // ...
}

Any action dispatched will run the reducer to immutably update the state. This is great for complex state transitions that useState isn‘t suited for.

useCallback to Memoize Callbacks

The useCallback Hook allows creating callback functions that remain unchanged between renders:

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

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

  return <Child onClick={increment} />;
}

function Child({ onClick }) {
  // ...
}

useCallback will return the same callback instance when setCount hasn‘t changed.

This avoids passing a new callback to Child causing unnecessary re-renders.

useMemo to Memoize Computations

The useMemo Hook allows expensive computations to be cached between renders:

function MyComponent({ items }) {
  const filteredItems = useMemo(() => {
    return items.filter(i => i.isActive);
  }, [items]);

  return <div>{filteredItems}</div>
}

useMemo will skip recomputing the filtered items when items hasn‘t changed.

This helps avoid expensive computations on each render.

Custom Hooks for Logic Reuse

You can create custom Hooks for reusing stateful logic between components.

For example, a custom useFetch hook:

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

  useEffect(() => {
    fetch(url)
      .then(res => res.json())
      .then(setData);
  }, [url]);

  return data;
}

function MyComponent() {
  const data = useFetch(‘/my/url‘);
  // ...
}

Custom Hooks let you move complex logic out of components into reusable functions.

Hooks Best Practices

Here are some tips I‘ve learned for working with Hooks effectively:

  • Start small by adding Hooks to non-critical new components first
  • Colocate stateful logic in one place instead of spreading across Hooks
  • Use useCallback/useMemo carefully for optimizations
  • Create custom Hooks early for reusable logic between components
  • Avoid using Hooks inside conditional or nested functions
  • Use the React Hooks ESLint plugin to catch issues
  • Learn by examining open source Hooks in libraries like Material-UI

Follow these best practices and you‘ll be leveraging Hooks like a pro in no time!

Key Takeaways on React Hooks

We‘ve covered a lot of ground! Here are the key points:

  • Hooks like useState provide direct access to React features in functions
  • They offer a simpler alternative to classes without sacrificing capabilities
  • Custom Hooks allow you to reuse stateful logic between components
  • Hooks excel when you need to share stateful logic and avoid abstraction
  • There are a couple essential rules around using Hooks correctly
  • Hooks allow a more functional programming style in React

I hope this guide has shed light on how React Hooks work and when you should use them. They represent an exciting advancement in writing React components with minimal abstraction.

The React community is rapidly adopting Hooks as a cleaner approach compared to classes. I encourage you to start experimenting with them in your own projects to see the benefits first-hand.

As always, feel free to reach out to me on Twitter if you have any other questions!

AlexisKestler

Written by Alexis Kestler

A female web designer and programmer - Now is a 36-year IT professional with over 15 years of experience living in NorCal. I enjoy keeping my feet wet in the world of technology through reading, working, and researching topics that pique my interest.