# How React Hooks Changed the Way We Write Components

> React 16.8 introduced the Hooks API — here's how it shifted component authoring from class-based to functional, what the advantages are, and what to watch out for.

- Published: 2019-03-03
- Category: Interface
- Tags: JavaScript, React
- Reading time: 4 min read
- Source: https://www.muhammetsafak.com.tr/en/blog/how-react-hooks-changed-the-way-we-write-components/
- Language: en-US
- Author: Muhammet Şafak

---
React 16.8 shipped in February 2019, and hooks became stable. I had been following the announcement since Dan Abramov's talk at React Conf in October 2018 — the problems he described were painfully familiar: logic scattered across lifecycle methods, bloated class components, the endless confusion around `this`. I have been using hooks in real projects for a few weeks now and wanted to share my thoughts.

## What Was Exhausting About Class Components?

The most tedious part of writing a class component was the way related logic ended up split across different lifecycle methods. Take a subscription, for example: you start it in `componentDidMount`, tear it down in `componentWillUnmount`, and restart it in `componentDidUpdate` whenever the id changes. Three separate methods in three separate places, yet all of them belong to the same "subscription management" concern.

```jsx
class UserProfile extends React.Component {
  componentDidMount() {
    this.subscription = userService.subscribe(this.props.userId, this.handleUpdate);
  }

  componentDidUpdate(prevProps) {
    if (prevProps.userId !== this.props.userId) {
      this.subscription.unsubscribe();
      this.subscription = userService.subscribe(this.props.userId, this.handleUpdate);
    }
  }

  componentWillUnmount() {
    this.subscription.unsubscribe();
  }

  handleUpdate = (data) => {
    this.setState({ user: data });
  };

  render() {
    return <div>{this.state.user?.name}</div>;
  }
}
```

The same logic with hooks:

```jsx
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    const subscription = userService.subscribe(userId, setUser);
    return () => subscription.unsubscribe();
  }, [userId]);

  return <div>{user?.name}</div>;
}
```

Everything lives together. The function returned from `useEffect` acts as the cleanup. The effect re-runs whenever `userId` changes. This is the exact equivalent of what the class version was doing — but without the fragmentation.

## useState and useEffect: Basic, but Sufficient

Two hooks cover a surprisingly wide surface area. `useState` is straightforward: it returns a value and a function to update it. `useEffect` handles side effects — data fetching, subscriptions, timers, and direct DOM manipulation.

The dependency array, `useEffect`'s second argument, is a critical concept. Leave it empty and the effect runs only on the initial render. Provide values and the effect re-runs whenever those values change. Omit it entirely and the effect runs on every render — which is rarely what you want.

```jsx
// Only on initial render:
useEffect(() => {
  fetchInitialData();
}, []);

// On every render (use with caution):
useEffect(() => {
  document.title = `${count} items`;
});

// Whenever count changes:
useEffect(() => {
  saveToStorage(count);
}, [count]);
```

## Custom Hooks: Sharing Logic the Right Way

The most valuable aspect of hooks is the ability to write custom hooks. Any function whose name starts with `use` can be a hook; inside it you can call other hooks. This is far cleaner than the mixin or Higher-Order Component (HOC) alternatives.

```jsx
function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    const handler = () => setWidth(window.innerWidth);
    window.addEventListener('resize', handler);
    return () => window.removeEventListener('resize', handler);
  }, []);

  return width;
}

// Usable in any component:
function Header() {
  const width = useWindowWidth();
  return <header>{width > 768 ? <DesktopNav /> : <MobileNav />}</header>;
}
```

In the class-based world I would have reached for an HOC or a render prop to share this kind of logic. Both work, but both create wrapper hell.

## Things I Have Had to Watch Out For

A few pitfalls caught me when I first started using hooks:

**Stale closure problem.** If you access props or state inside `useEffect` and forget to include them in the dependency array, you can end up reading stale values. ESLint's `exhaustive-deps` rule catches most of these cases — I strongly recommend keeping it enabled.

**Not reaching for useEffect for every side effect.** You do not need `useEffect` to fetch data inside a click handler — you can call it directly from the event handler. `useEffect` is for side effects that are a genuine consequence of rendering.

**Not rushing to rewrite class components.** The React team itself says it: class components are not going away. Rather than spending time migrating existing, working class components to hooks, it makes more sense to write all new code with hooks going forward.

## In Summary

Hooks move React toward a more functional place. They allow logic to be grouped by related concept rather than by lifecycle method. I see this change as an improvement, not a revolution. Does it solve every problem that class components introduced? No — but it meaningfully addresses the most common pain points. After a few weeks I have no desire to go back.

That said, when I introduce hooks to a new team member, I still make a point of explaining what class components were solving first. The conveniences hooks bring only make sense when you can see the problem behind them. Using a tool without knowing what it replaced is using it at half capacity — and in a fast-moving ecosystem like React, that gap becomes consequential within a few years.
