跳至主内容区

更新模式

[非官方测试版翻译]

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

在 Immer 出现之前,操作不可变数据意味着必须掌握各种不可变更新模式。

为了帮助开发者"忘记"这些模式,本文将概述如何利用 JavaScript 内置 API 更新对象和集合:

对象变更

import {produce} from "immer"

const todosObj = {
id1: {done: false, body: "Take out the trash"},
id2: {done: false, body: "Check Email"}
}

// add
const addedTodosObj = produce(todosObj, draft => {
draft["id3"] = {done: false, body: "Buy bananas"}
})

// delete single property
const deletedTodosObj = produce(todosObj, draft => {
delete draft["id1"]
})

// update
const updatedTodosObj = produce(todosObj, draft => {
draft["id1"].done = true
})

// replace & update in bulk
const updatedTodosObj = produce(todosObj, draft => {
Object.assign(draft, {
id1: {done: true, body: "Take out the trash"},
id2: {done: true, body: "Check Email"},
id3: {done: true, body: "Feed my cat"}
})

// reset/clear/empty
const emptyTodo = produce(todosObj, () => {
return {};
})

每当嵌套的草稿字段获得新引用或值时,produce() 将完成不可变更新的应用并返回新引用。如果尝试变更但值保持不变,Immer 将放弃变更并返回 produce() 中的现有引用。

数组变更

import {produce} from "immer"

const todosArray = [
{id: "id1", done: false, body: "Take out the trash"},
{id: "id2", done: false, body: "Check Email"}
]

// add
const addedTodosArray = produce(todosArray, draft => {
draft.push({id: "id3", done: false, body: "Buy bananas"})
})

// delete by index
const deletedTodosArray = produce(todosArray, draft => {
draft.splice(3 /*the index */, 1)
})

// update by index
const updatedTodosArray = produce(todosArray, draft => {
draft[3].done = true
})

// insert at index
const updatedTodosArray = produce(todosArray, draft => {
draft.splice(3, 0, {id: "id3", done: false, body: "Buy bananas"})
})

// remove last item
const updatedTodosArray = produce(todosArray, draft => {
draft.pop()
})

// remove first item
const updatedTodosArray = produce(todosArray, draft => {
draft.shift()
})

// add item at the beginning of the array
const addedTodosArray = produce(todosArray, draft => {
draft.unshift({id: "id3", done: false, body: "Buy bananas"})
})

// delete by id
const deletedTodosArray = produce(todosArray, draft => {
const index = draft.findIndex(todo => todo.id === "id1")
if (index !== -1) draft.splice(index, 1)
})

// update by id
const updatedTodosArray = produce(todosArray, draft => {
const index = draft.findIndex(todo => todo.id === "id1")
if (index !== -1) draft[index].done = true
})

// filtering items
const updatedTodosArray = produce(todosArray, draft => {
// creating a new state is simpler in this example
// (note that we don't need produce in this case,
// but as shown below, if the filter is not on the top
// level produce is still pretty useful)
return draft.filter(todo => todo.done)
})

// reset/clear/empty
const emptyTodo = produce(todosArray, () => {
return [];
})

嵌套数据结构

import {produce} from "immer"

// example complex data structure
const store = {
users: new Map([
[
"17",
{
name: "Michel",
todos: [
{
title: "Get coffee",
done: false
}
]
}
]
])
}

// updating something deeply in-an-object-in-an-array-in-a-map-in-an-object:
const nextStore = produce(store, draft => {
draft.users.get("17").todos[0].done = true
})

// filtering out all unfinished todo's
const nextStore = produce(store, draft => {
const user = draft.users.get("17")
// when filtering, creating a fresh collection is simpler than
// removing irrelevant items
user.todos = user.todos.filter(todo => !todo.done)
})

注意:许多数组操作可通过传递多个参数或使用展开语法一次性插入多个元素:todos.unshift(...items)

注意:当处理包含通过 ID 标识对象的数组时,建议使用 Map 或基于索引的对象(如上所示),而非频繁执行查找操作,因为查找表通常性能更优。