You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

通用更新模式(General Update Pattern)是否存在通用实现方案?

通用数组状态的更新模式实现方案

D3.js的通用更新模式通过分离进入、更新、退出三个阶段的逻辑,处理数据数组对HTML/SVG DOM树的修改。现在需要明确:是否存在类似的通用更新模式实现,能够直接针对数组这类通用状态数据进行更新?

以下是可行的实现思路与方案:


一、手动实现轻量链式API(如示例扩展)

你给出的示例已经是核心思路的雏形,核心是通过键函数匹配新旧数据条目,分别处理新增、更新、删除逻辑。以下是完善后的可运行实现:

const state = [{id:1, data:"foo", data2:"foo"}, {id:2, data:"bar"}]
const change = [{id:1, data:"baz"}, {id:3, data:"qux"}]

const nextState = select(state, entry => entry.id)
    .update(change, entry => entry.id)
    .join(
        enter => enter,       // 保留新增条目
        update => Object.assign(update.current, update.change), // 合并新旧数据
        exit => null          // 移除退出条目
    )
    .value()

console.log(nextState)
// 输出: [{id:1, data:"baz", data2:"foo"}, {id:3, data: "qux"}]

// 完整实现
function select(state, keyF) {
    const stateMap = new Map(state.map(entry => [keyF(entry), entry]))
    let changeMap = new Map()

    const selection = {
        update(change, key) {
            changeMap = new Map(change.map(entry => [key(entry), entry]))
            return selection
        },
        join(enterFn, updateFn, exitFn) {
            // 处理更新条目:匹配到的新旧数据合并
            for (const [key, current] of stateMap) {
                const changeEntry = changeMap.get(key)
                if (changeEntry) {
                    const result = updateFn({ current, change: changeEntry })
                    if (result === null) stateMap.delete(key)
                }
            }
            // 处理进入条目:新增未存在的条目
            for (const [key, entry] of changeMap) {
                if (!stateMap.has(key)) {
                    const result = enterFn(entry)
                    if (result !== null) stateMap.set(key, result)
                }
            }
            // 处理退出条目:移除未匹配的旧条目
            for (const [key, entry] of stateMap) {
                if (!changeMap.has(key)) {
                    const result = exitFn(entry)
                    if (result === null) stateMap.delete(key)
                }
            }
            return selection
        },
        value() {
            return Array.from(stateMap.values())
        }
    }
    return selection
}

二、借助现有工具库实现

如果不想手动造轮子,可以利用现成工具库简化逻辑:

  • Immer:通过produce函数以Immutable风格处理数组增删改,语法简洁,无需手动维护映射关系
  • Ramda:组合R.mergeWithKey、R.differenceWith等函数,用函数式风格实现匹配更新逻辑
  • Redux Toolkit:在React状态管理场景中,内置的Immer支持可以轻松处理数组的键匹配更新,适合大型项目

三、纯函数风格实现

如果偏好无副作用的纯函数写法,可以直接编写独立函数处理:

function updateArrayState(prevState, newData, keyFn, handlers) {
    const {
        onEnter = entry => entry,
        onUpdate = (prev, next) => Object.assign({}, prev, next),
        onExit = () => null
    } = handlers || {}

    const prevMap = new Map(prevState.map(item => [keyFn(item), item]))
    const newMap = new Map(newData.map(item => [keyFn(item), item]))
    
    const result = []
    // 处理更新与新增
    newMap.forEach((newItem, key) => {
        const prevItem = prevMap.get(key)
        if (prevItem) {
            const updated = onUpdate(prevItem, newItem)
            updated && result.push(updated)
        } else {
            const entered = onEnter(newItem)
            entered && result.push(entered)
        }
    })
    // 处理退出(如需保留未删除的旧条目)
    prevMap.forEach((prevItem, key) => {
        if (!newMap.has(key)) {
            const exited = onExit(prevItem)
            exited && result.push(exited)
        }
    })
    return result
}

// 使用示例
const nextState = updateArrayState(state, change, item => item.id, {
    onUpdate: (prev, next) => Object.assign({}, prev, next),
    onExit: () => null
})
console.log(nextState)

内容的提问来源于stack exchange,提问作者Simon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 08:10:28