Master React: Say Goodbye to 7 Costly Mistakes Forever!
Tags: webdev, javascript, programming, react
Published: 2023-10-21 16:41:45


React is a very powerful library for creating UI. With this power, you are responsible for crafting readable and bug-free code, which means that you have to keep up with best practices and patterns. The easiest way to become a better developer is to learn from your mistakes, or even other people's.

In this article, based on my actual work experience, you'll learn how to avoid 7 common mistakes that many developers make.

1. Calling a function that takes no arguments in an event handler.
2. Using `useEffect` to transform rendering data.
3. Resetting the state of a component using `useEffect`.
4. Not using `key` in lists.
5. Modify the state value directly by assignment.
6. Not using the previous state when calling setState.
7. Forgetting to clean up side effects in `useEffect` .

## 1. Calling a function that takes no arguments in an event handler

### ❌ The wrong way

```tsx
export function App() {
 const [clapped, setClapped] = useState(false);

 const clap = () => setClapped(true);

 return (
  <div>
   <p>Support this author: {clapped ? 'yes' : 'no'}</p>
   <button onClick={(ev: MouseEvent) => clap()}>Clap</button>
  </div>
 );
}
```

### ✅ The right way

```tsx
export function App() {
 const [clapped, setClapped] = useState(false);

 const clap = () => setClapped(true);

 return (
  <div>
   <p>Support this author: {clapped ? 'yes' : 'no'}</p>
   <button onClick={clap}>Clap</button>
  </div>
 );
}
```

###🎈 Explanation

We can simply provide the function, without calling it. This works because React will take that function and call it internally with the event object as an argument. Our function takes no argument, so the event object will be discarded.

## 2. Using useEffect to transform rendering data

### ❌ The wrong way

```tsx
function Form() {
  const [firstName, setFirstName] = useState('Miroiu');
  const [lastName, setLastName] = useState('Dev');
  const [fullName, setFullName] = useState('');

  useEffect(() => {
    setFullName(firstName + ' ' + lastName);
  }, [firstName, lastName]);
  // ...
}
```

### ✅ The right way

```tsx
function Form() {
  const [firstName, setFirstName] = useState('Miroiu');
  const [lastName, setLastName] = useState('Dev');

  const fullName = firstName + ' ' + lastName;
  // ...
}
```

### 🎈 Explanation

Using unnecessary effects can lead to bugs or even infinite loops in your application. You can create new variables based on the state or props of your component that will change on render, without using an effect.

## 3. Resetting the state of a component using useEffect

### ❌ The wrong way

```tsx
export default function Form({ formId }) {
  const [name, setName] = useState('');

  useEffect(() => {
    setName('');
  }, [formId]);
  // ...
}
```

### ✅ The right way

```tsx
export default function Page({ formId }) {
  return (
    <Form
      formId={formId}
      key={formId}
    />
  );
}
```

### 🎈 Explanation

Keys identify and track items. This is important for React's virtual DOM reconciliation process, which efficiently updates the user interface by minimizing unnecessary re-renders and improving performance.

When we formId changes, our key changes as well. React sees this and thinks the Form component is a new component, so it mounts it again and resets the state.

## 4. Not using key in lists

### ❌ The wrong way

```tsx
export default function UserList({ users }) {
  return (
    {users.map(user => <User {...user}/>)}
  );
}
```

### ✅ The right way

```tsx
export default function UserList({ users }) {
  return (
    {users.map(user => <User key={user.id} {...user}/>)}
  );
}
```

### 🎈 Explanation

This wouldn't be a problem if users never changes, React will resort to using the element's index as a default `key`. If we perform operations such as add or remove, then we really need to provide a `key`. React will know which elements have been added, removed, or changed when the list is re-rendered. This results in better performance, as React can minimize the number of DOM manipulations needed. Using the `key` prop will prevent many bugs.

## 5. Modify the state value directly by assignment

### ❌ The wrong way

```tsx
export default function User() {
  const [name, setName] = useState(''); 

  const handleOnChange = (ev: ChangeEvent<HTMLInputElement>) => {
     name = ev.target.value
  }

  return (
    <input onChange = {handleOnChange} />
   );
}
```

### ✅ The right way

```tsx
export default function User() {
  const [name, setName] = useState(''); 

  const handleOnChange = (ev: ChangeEvent<HTMLInputElement>) => {
    setName(ev.target.value);
  }

  return (
    <input onChange = {handleOnChange} />
   );
}
```

### 🎈 Explanation

Updating the state directly will not trigger a re-render. React may not recognize the change when the state is modified directly, and it can result in bugs and issues. To ensure the proper behavior and reactivity of your React components, you should always use the provided state update mechanisms.

## 6. Not using the previous state when calling setState

### ❌ The wrong way

```tsx
export default function UserList() {
  const [users, setUsers] = useState<User[]>([]);

  const addUser = (user: User) =>
    users.push(user);
    setUser(users);
  }

  //...
}
```

### ✅ The right way

```tsx
export default function UserList() {
  const [users, setUsers] = useState<User[]>([]);

  const addUser = (user: User) =>
    setUser(prevUsers => [...prevUsers, user]);
  }

  //...
}
```

### 🎈 Explanation

Using the correct method with the function that takes the `prevUsers` as an argument ensures that you're always working with the latest state, preventing race conditions and issues that may arise from concurrent state updates. This is particularly important in situations where multiple state updates may occur in rapid succession or in response to user interactions.

## 7. Forgetting to clean up side effects in useEffect

### ❌ The wrong way

```tsx
export default function UserList() {
   const [users, setUsers] = useState<User[]>([]);
   
   const onAddUser = (user: User) => {
      setUsers(prevUsers => [...prevUsers, user]);
   }

   useEffect(() => {
      socket.on('add-user', onAddUser);
   },[])
  //...
}
```

### ✅ The right way

```tsx
export default function UserList() {
   const [users, setUsers] = useState<User[]>([]);
   
   const onAddUser = (user: User) => {
      setUsers(prevUsers => [...prevUsers, user]);
   }

   useEffect(() => {
      socket.on('add-user', onAddUser);
      
      return () => {
        socket.off('add-user', onAddUser);
      }
   }, [])
  //...
}
```

### 🎈 Explanation

Forgetting to unsubscribe can lead to unexpected bugs and memory leakage. In this example when the socket acts on the add-user event, because we did not unsubscribe, onAddUser will be called twice. Also every time we left the page, and came back, another subscription to the add-user event will be added, which results in memory leakage.
It is always a best practice to unsubscribe when possible.

## Thank you for reading!

If you enjoyed this article, please clap a few times or share it with your awesome friends.

If you like this content, feel free to follow me on [medium](https://medium.com/@miroiu-dev).

More posts like this:

[Supercharge Your React Components with This Powerful Pattern](https://blog.stackademic.com/use-this-pattern-to-10x-your-react-components-64ed475f86e7])

[Master Data Fetching in React with This Easy Guide](https://blog.stackademic.com/master-data-fetching-in-react-with-this-easy-guide-ed141db41d15)

[Mastering JavaScript Classes: Your Shortcut to OOP Success!](https://blog.stackademic.com/mastering-javascript-classes-your-shortcut-to-oop-success-3b877433db88)