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;
  });
};

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read full post →

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?

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more