DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 966,904 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
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 Author

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?

This post blew up on DEV in 2020:

js visualized

πŸš€βš™οΈ JavaScript Visualized: the JavaScript Engine

As JavaScript devs, we usually don't have to deal with compilers ourselves. However, it's definitely good to know the basics of the JavaScript engine and see how it handles our human-friendly JS code, and turns it into something machines understand! πŸ₯³

Happy coding!