DEV Community

Ioannis Potouridis
Ioannis Potouridis

Posted on

5 3

๐ŸŽถ 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;
  });
};

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

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?