Movatterモバイル変換


[0]ホーム

URL:


Skip to main content
Support Ukraine 🇺🇦Help Provide Humanitarian Aid to Ukraine.

React & Immer

egghead.io lesson 8: Using Immer with _useState_. Or: _useImmer_

useState + Immer

TheuseState hook assumes any state that is stored inside it is treated as immutable. Deep updates in the state of React components can be greatly simplified as by using Immer. The following example shows how to useproduce in combination withuseState, and can be tried onCodeSandbox.

importReact,{ useCallback, useState}from"react";
import{produce}from"immer";

constTodoList=()=>{
const[todos, setTodos]=useState([
{
id:"React",
title:"Learn React",
done:true
},
{
id:"Immer",
title:"Try Immer",
done:false
}
]);

const handleToggle=useCallback((id)=>{
setTodos(
produce((draft)=>{
const todo= draft.find((todo)=> todo.id=== id);
todo.done=!todo.done;
})
);
},[]);

const handleAdd=useCallback(()=>{
setTodos(
produce((draft)=>{
draft.push({
id:"todo_"+Math.random(),
title:"A new todo",
done:false
});
})
);
},[]);

return(<div>{*/ See CodeSandbox */}</div>)
}

useImmer

Since all state updaters follow the same pattern where the update function is wrapped inproduce, it is also possible to simplify the above by leveraging theuse-immer package that will wrap updater functions inproduce automatically:

importReact,{ useCallback}from"react";
import{ useImmer}from"use-immer";

constTodoList=()=>{
const[todos, setTodos]=useImmer([
{
id:"React",
title:"Learn React",
done:true
},
{
id:"Immer",
title:"Try Immer",
done:false
}
]);

const handleToggle=useCallback((id)=>{
setTodos((draft)=>{
const todo= draft.find((todo)=> todo.id=== id);
todo.done=!todo.done;
});
},[]);

const handleAdd=useCallback(()=>{
setTodos((draft)=>{
draft.push({
id:"todo_"+Math.random(),
title:"A new todo",
done:false
});
});
},[]);

// etc

For the full demo seeCodeSandbox.

useReducer + Immer

Similarly touseState,useReducer combines neatly with Immer as well, as demonstrated in thisCodeSandbox:

importReact,{useCallback, useReducer}from"react"
import{produce}from"immer"

constTodoList=()=>{
const[todos, dispatch]=useReducer(
produce((draft, action)=>{
switch(action.type){
case"toggle":
const todo= draft.find(todo=> todo.id=== action.id)
todo.done=!todo.done
break
case"add":
draft.push({
id: action.id,
title:"A new todo",
done:false
})
break
default:
break
}
}),
[
/* initial todos */
]
)

const handleToggle=useCallback(id=>{
dispatch({
type:"toggle",
id
})
},[])

const handleAdd=useCallback(()=>{
dispatch({
type:"add",
id:"todo_"+Math.random()
})
},[])

// etc
}

useImmerReducer

...which again, can be slightly shorted byuseImmerReducer from theuse-immer package (demo):

importReact,{ useCallback}from"react";
import{ useImmerReducer}from"use-immer";

constTodoList=()=>{
const[todos, dispatch]=useImmerReducer(
(draft, action)=>{
switch(action.type){
case"toggle":
const todo= draft.find((todo)=> todo.id=== action.id);
todo.done=!todo.done;
break;
case"add":
draft.push({
id: action.id,
title:"A new todo",
done:false
});
break;
default:
break;
}
},
[/* initial todos */]
);

//etc

Redux + Immer

Redux + Immer is extensively covered in the documentation ofRedux Toolkit. For Redux without Redux Toolkit, the same trick as applied touseReducer above can be applied: wrap the reducer function withproduce, and you can safely mutate the draft!

For example:

import{produce}from"immer"

// Reducer with initial state
constINITIAL_STATE=[
/* bunch of todos */
]

const todosReducer=produce((draft, action)=>{
switch(action.type){
case"toggle":
const todo= draft.find(todo=> todo.id=== action.id)
todo.done=!todo.done
break
case"add":
draft.push({
id: action.id,
title:"A new todo",
done:false
})
break
default:
break
}
})

[8]ページ先頭

©2009-2025 Movatter.jp