Immer
Quick start
// API
produce(baseState, recipe: (draftState) => void): nextState
import { produce } from 'immer';
const nextState = produce(baseState, draft = > {
// Update draft here, no need return
draft[1].done = true
draft.push({title: "Tweet about it"})
});
How it works
Step-by-step
- Clone a
draft
object frombase
state - Update
draft
(not affectbase
) - Update state with
draft
Why using it?
Avoid spead operation hell ...
const handleUpdate = () => {
setSomethingState(prev => ({
...prev,
stateInside: {
...prev.stateInside,
newUpdate
}
}));
}
Using
React.useState
- Without Immer
- With Immer
const addTodo = (newTodo) => {
setState(prev => [...prev, newTodo])
}
const updateTodoStatus = (todoId) => {
setState(prev => prev.map(i => {
if (i.id === todoId) {
return { ...i, status: !i.status };
}
return i;
}))
}
const increaseUserVisit = (userId) => {
setState(prev => ({
[userId]: {
...prev[userId],
visitCount: prev[userId] + 1
}
}));
}
const addTodo = (newTodo) => {
setState(produce(draft => {
draft.push(newTodo);
}));
}
const updateTodoStatus = (todoId) => {
setState(produce(draft => {
const todo = draft.find(i => i.id === todoId);
if (todo) todo.status = !todo.status;
}));
}
const increaseUserVisit = (userId) => {
setState(produce(draft => {
draft[userId].visitCount += 1;
}));
}
Redux - Reducer
- Without Immer
- With Immer
const byId = (state, action) => {
switch (action.type) {
case RECEIVE_PRODUCTS:
return {
...state,
...action.products.reduce((obj, product) => {
obj[product.id] = product
return obj
}, {})
}
default:
return state
}
}
const byId = (state, action) =>
produce(state, draft => {
switch (action.type) {
case RECEIVE_PRODUCTS:
action.products.forEach(product => {
draft[product.id] = product
})
}
});