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:
- Adding a new todo
- Deleting a todo
- Editing a todo (its status or text)
And I'm going to write three ways doing that:
- The immutable way
- Using
immer
'sproduce
- Using
useImmer
hook fromuse-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)
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
andj
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 :)Thank you for your comment. I never use
i
andj
. I did that in order to make some functions appear shorter. In real projects I'd useindex
,todoIndex
etc.Help, how do I mute users from my feed?