immerというJavaScriptのライブラリの使い方を調べた
immerは、JavaScript でイミュータブルなデータ構造を簡単に操作するためのライブラリ
proxy を使って、オブジェクトを一時的に「仮想的にミュータブルに扱える」ようにしてくれる
よく使うのは ReactのuseStateでの操作で、状態を更新する処理が簡潔に書けるようになる
const handleToggle = useCallback((id) => { setTodos( produce((draft) => { const todo = draft.find((todo) => todo.id === id); todo.done = !todo.done; }) ); }, []);
またuseReducerでの操作も簡潔になる
const TodoList = () => { 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() }) }, []) }
immerが特に便利なのは、ネストされたオブジェクトの操作
たとえば、普通にネストされたオブジェクトを操作すると、毎回手動で全ての親オブジェクトをコピーしなければならない
const state = { user: { profile: { name: 'Alice', age: 25, }, }, }; const newState = { ...state, user: { ...state.user, profile: { ...state.user.profile, name: 'Bob', }, }, };
このコード、変更箇所は name だけなのに、コピーが多くて面倒である
こんな時にimmer を使うと、ネストされたオブジェクトのコピーを気にせず、以下のように書ける
import produce from 'immer'; const state = { user: { profile: { name: 'Alice', age: 25, }, }, }; const newState = produce(state, draft => { draft.user.profile.name = 'Bob'; });