16-Dec

React

Put event logic in event handlers in React

Changing the state of your component based on the interaction of a user is a common scenario in a React application. Your first instinct may be to put the event logic in a useEffect that depends on some value to change so the effect can be triggered. But why put the event-specific logic some other place than event handlers? To avoid misbehavior in your application, put event logic in the event handlers and not in useEffects.

3 min read

·

By Caroline Odden

·

December 16, 2022

The content of this article is heavily inspired by the new React docs (which is in beta).

Event logic in useEffects

When a user is interacting with your website you want to trigger something based on what action the user takes. Clicking, selecting, submitting, and other interactions you can think of, can change the state of your application, and maybe you want to trigger a function after the user has interacted with the website.

For instance, say you have an application and the user can add a number of likes to an article. You have an Article component that has article and hasSetNumberOfLikes as props, where article is the content of the article, and hasSetNumberOfLikes is a boolean value telling your component that the article has gotten a number of likes submitted from the user. The component has an event handler that receives a number of likes and calls a function that submits the number of likes to the article. Further on the component has a useEffect that listens to the value of hasSetNumberOfLikes to check if a user has submitted the number of likes, and then sends a notification to the user that the submission was successful. Take a look at the example below:

function Article({ article, hasSetNumberOfLikes }) {
  useEffect(() => {
    if (hasSetNumberOfLikes) {
      sendNotification()
    }
  }, [hasSetNumberOfLikes])
  
  const addLikes = (numberOfLikes) => {
    submitNumberOfLikes(numberOfLikes)
  }
  
  /* Rest of component */
}

In the code above you may get some unwanted behavior from the useEffect. Odds are that the effect will be invoked too many times. For instance, if you cached the state of your application, the notification can be sent to the user several times when the user refreshes or navigates back and forth. This is quite annoying as a user when getting bombarded with several notifications for one event you performed. By using useEffects this way, you misuse the intended use case for the useEffect hook. Remember that useEffects are for side effects, not maintaining your local state or props.

You don't need useEffects everywhere

The useEffect hook in React is a JavaScript function which is intended to synchronise your effects with the state in your component. Side effects are functions used to communicate with things outside of your component.

Some common examples are data fetching or timing functions like setTimeout and setInterval.

Event logic in event handlers

The best solution is to get rid of the useEffect and rather put the event logic as close to the event handlers as possible. It requires less code, and less code is always better 😏. If you feel you are writing repetitive code and it makes you want to put it in a useEffect because "it is simpler" and requires less code, it may be better to extract your code to functions with logic that can be shared between several event handlers. Take a look at the example below:

function Article({ article }) {
  const addLikes = (numberOfLikes) => {
      submitNumberOfLikes(numberOfLikes)
      sendNotification()
  }
  
  /* Rest of component */
}

I realize that it is not always that easy to just "move code close to the event handler", especially in old legacy code bases. However, I think this is something we need to pursue when writing new code or rewriting something that is old, to make reliable applications.

TL;DR

Drop the useEffects for event logic, put all the event-specific logic as close as possible to the event handlers.

Up next...

Loading…