通用更新模式(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
相关产品推荐
相关产品推荐

