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?