🎶 Toss a coin to your... handler 💰
Tags: javascript, react, tutorial
Published: 2020-01-24 15:56:07

I am not Eminem.

So, I am not good at saying 7.6 words per second.

I am a React developer.

I barely say 0 words per second.

I do 7.6 state updates per second.

I make To-do apps.

This is how I made my handlers more readable with `use-immer`.

This is my Todo component.

```react
import React from 'react';

function Todo({ completed, onChange, onDelete, text }) {
  return (
    <div>
      <input
        checked={completed}
        name="completed"
        onChange={onChange}
        type="checkbox"
      />
      <input name="text" onChange={onChange} type="text" value={text} />
      <button onClick={onDelete}>Delete</button>
    </div>
  );
}

export default Todo;
```

This is my App component.

```react
import React, { useState } from 'react';

import Todo from './Todo';

function App() {
  const [todos, setTodos] = useState([]);
  // const [todos, setTodos] = useImmer([]);

  // imagine handlers here

  return (
    <>
      {todos.map(({ completed, text }, index) => (
        <Todo
          completed={completed}
          key={index}
          onChange={handleTodoChange(index)}
          onDelete={handleTodoDelete(index)}          
          text={text}
        />
      ))}

      <button onClick={handleTodoAdd}>Add todo</button>
    </>
  ) 
}

export default App;
```

I need three handlers for:

1. Adding a new todo
2. Deleting a todo
3. Editing a todo (its status or text)

And I'm going to write three ways doing that:

1. The immutable way
2. Using `immer`'s `produce`
3. Using `useImmer` hook from `use-immer`.

For people that are not familiar with `immer`,`produce` is a function that provides a draft for you to mutate and produces the next immutable state.

`useImmer` is similar to `useState` except that the updater function provides you the draft that can be mutated.


#### Adding a todo

The immutable way:

```react
const handleTodoAdd = () => {
  setTodos(prev => [...prev, { completed: false, text: "" }]);
}
```

Using `produce`:

```react
const handleTodoAdd = () => {
  setTodos(prev =>
    produce(prev, draft => {
      draft.push({ completed: false, text: "" });
    })
  );
}
```

Using `useImmer`:

```react
const handleTodoAdd = () => {
  setTodos(draft => {
    draft.push({ completed: false, text: "" });
  });
}
```

#### Deleting a todo

The immutable way:

```react
const handleDeleteClick = i => () => {
  setTodos(prev => prev.filter((_, j) => j !== i));
}
```

Using `produce`:

```react
const handleDeleteClick = i => () => {
  setTodos(prev =>
    produce(prev, draft => {
      draft.splice(i, 1);
    })
  );
}
```

Using `useImmer`:

```react
const handleDeleteClick = i => () => {
  setTodos(draft => {
    draft.splice(i, 1);
  });
}
```

#### Editing a todo

The immutable way:

```react
const handleTodoChange = i => ({ target }) => {
  const value = target.type === "checkbox" ? target.checked : target.value;

  setTodos(prev =>
    prev.map((todo, j) => {
      if (j === i) {
        return {
          ...todo,
          [target.name]: value
        };
      }

      return todo;
    })
  );
};
```

Using `produce`:

```react
const handleTodoChange = i => ({ target }) => {
  const value = target.type === "checkbox" ? target.checked : target.value;

  setTodos(prev =>
    produce(prev, draft => {
      draft[i][target.name] = value;
    })
  );
};
```

Using `useImmer`:

```react
const handleTodoChange = i => ({ target }) => {
  const value = target.type === "checkbox" ? target.checked : target.value;

  setTodos(draft => {
    draft[i][target.name] = value;
  });
};
```