Don't useEffect as callback!
Tags: react, javascript, beginners, webdev
Published: 2020-04-12 12:40:33

---
title: Don't useEffect as callback!
published: true
description: The useEffect hook in React is sometimes not easy to understand. It can be hard to get it working properly. In this blog post, we'll have a look at a common misuse of useEffect.
tags: react, javascript, beginners, webdev
canonical_url: https://jkettmann.com/dont-useeffect-as-callback/
---

The `useEffect` hook in React is sometimes not easy to understand. It can be hard to get it working properly. You might have missing dependencies, cause a stale closure or an infinite loop.

In this blog post, we'll have a look at a common misuse of the `useEffect` hook. It doesn't cause an error, but it causes unnecessary re-renders and code complexity. We'll see two examples where `useEffect` is basically a callback and how you can simplify code like that.

## 1. State update triggering a callback

This is a simple example component where a state update is supposed to trigger a callback. This is, of course, a broken-down component. But I saw this pattern often enough in more complex components in real code.

```js
function Form({ onUpdate }) {
  const [email, setEmail] = useState('');
  const firstRender = useRef(true);

  useEffect(() => {
    if (firstRender.current) {
      firstRender.current = false;
      return;
    }
    onUpdate(email);
  }, [onUpdate, email]);

  return (
    <form>
      <input
        value={email}
        onChange={(e) => setEmail(() => e.target.value)}
        name="email"
      />
    </form>
  );
}
```

We have an input inside a form. The component keeps track of the `email` value in a state variable. We want the `onUpdate` prop to be called whenever the `email` changes.

One option is to `useEffect` with `email` as a dependency like in the example above. There are two problems with this approach:

1. It obfuscates the connection between the `onChange` handler of the input component and the `onUpdate` prop passed to this component by its parent.
2. We need the workaround with the `useRef` to prevent a call of `onUpdate` on the first render.

> **Note: There is a very valid use-case** though that I missed in my first version. In some cases, you might want the `onUpdate` function only to be called **after** the state has been set, similar to using the second parameter of a class component's `this.setState(state, callback)`. In this case, `useEffect` is the way to go, in fact.

The alternative approach is very simple: We use a function instead.

```js
function Form({ onUpdate }) {
  const [email, setEmail] = useState('');

  const onChange = (e) => {
    const { value } = e.target;
    setEmail(value);
    onUpdate(value);
  };

  return (
    <form>
      <input
        value={email}
        onChange={onChange}
        name="email"
      />
    </form>
  );
}
```

Now it's immediately clear that `setEmail` and `onUpdate` are coupled together. We also got rid of the `useRef`.

In my opinion that's much cleaner and easier to comprehend.

## 2. Transforming data

Another common example of an unnecessary case of `useEffect` is again related to a state update. This time it's used to transform data though.

Have a look at the following example.

> I took this example from a [new course about becoming job-ready for working in professional dev teams](https://ooloo.io). Check it out if you're interested, I'll launch it soon.

```js
function RedditPosts() {
  const [data, setData] = useState(null);
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    fetch('https://www.reddit.com/r/javascript/top.json?t=day&limit=10')
      .then(response => response.json())
      .then(({ data }) => setData(data));
  }, []);

  useEffect(() => {
    if (!data) {
      return;
    }

    const mappedPosts = data.children.map(post => post.data);
    setPosts(mappedPosts);
  }, [data]);

  return (
    <div>
      {
        posts.map(post => (
          <div key={post.id}>{post.title}</div>
        ))
      }
    </div>
  );
}
```

So what's happening here? We have two `useEffect`. The first one is triggered when the component did mount. It fetches data from an API and stores it inside a state variable.

And what about the second `useEffect`? That one is triggered when the `data` state has updated. It transforms the data object into an array of posts to prepare it for rendering. And again, we need a workaround to not run the effect on the first render: the check for `!data`.

How does an alternative approach look like? We can completely get rid of the `data` state and handle the transformation after the API request.

This is how it looks like.

```js
function RedditPosts() {
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    fetch('https://www.reddit.com/r/javascript/top.json?t=day&limit=10')
      .then(response => response.json())
      .then(({ data }) => data.children.map(post => post.data))
      .then((mappedPosts) => setPosts(mappedPosts));
  }, []);

  return (
    <div>
      {
        posts.map(post => (
          <div key={post.id}>{post.title}</div>
        ))
      }
    </div>
  );
}
```

We got rid of the second `useEffect` and handle the transformation in the first one together with the API request.

Much simpler!

## Wrapping it up

There are, of course, lots of valid use-cases for `useEffect`, but those two are not among them. If you recognized yourself using `useEffect` in one of the above ways, try to stop for a moment and think about possible alternative approaches the next time.

If you liked this blog post and want to get update [subscribe to my list](https://jkettmann.com/subscribe/).

And if you're currently or in the near future **looking for a web development job** this free course might be interesting for you:

# [Boost your chances of getting a dev job](https://jkettmann.com/your-first-tech-job-email-course/)

Finding your first job as a software developer can be tough. You may apply dozens of times without even getting a reply.

If you're in that situation check out my free course. You'll learn how to **stand out of the crowd of applicants** with valuable insights about the hiring process behind the curtains, how to optimize your resume, create **outstanding portfolio projects** and many tips about **job interviews**.