DEV Community

Ioannis Potouridis
Ioannis Potouridis

Posted on

🎶 Toss a coin to your... handler 💰

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.

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.

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:

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

Using produce:

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

Using useImmer:

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

Deleting a todo

The immutable way:

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

Using produce:

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

Using useImmer:

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

Editing a todo

The immutable way:

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:

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:

const handleTodoChange = i => ({ target }) => { const value = target.type === "checkbox" ? target.checked : target.value; setTodos(draft => { draft[i][target.name] = value; }); }; 

Top comments (3)

Collapse
 
dansilcox profile image
Dan Silcox

Nice, as someone fairly inexperienced with react I can still follow what you’re doing and it looks quite similar to how the logic would be in say an angular component or something - may have to give this sort of thing a go some time :) one thing I would say is the i and j notation is not very readable - I know they’re a throw back to older languages and are quite well understood but it would be fairly trivial to make these a lot clearer especially to someone unfamiliar with the framework/library/context :)

Collapse
 
potouridisio profile image
Ioannis Potouridis

Thank you for your comment. I never use i and j. I did that in order to make some functions appear shorter. In real projects I'd use index, todoIndex etc.

Collapse
 
patryktech profile image
Patryk

🎶 Toss a coin to your...

Help, how do I mute users from my feed?