solid-record的redo函数无法正确设置状态问题求助
Solid-Record节点重父化Redo异常问题解决
问题描述
使用solid-record构建思维导图应用,实现节点重父化的撤销/redo功能时,撤销操作可正常恢复初始状态,但redo后节点0的子节点列表变为[3],而非预期的[1,2]。移除修改节点3parentid的操作后,redo功能恢复正常。
复现代码
import { Component, For } from 'solid-js' import { createStore } from 'solid-js/store' import { createHistory } from 'solid-record' type Node = { id: number, children: Array<number> parentid: number } const initialValue = [ { id: 0, children: [1, 2, 3], parentid: -1 }, { id: 1, children: [], parentid: 0 }, { id: 2, children: [], parentid: 0 }, { id: 3, children: [], parentid: 0 }, ] const [state, setState] = createStore(initialValue) const undoHistory = createHistory() const changeState = () => { undoHistory.batch() const nodeid = 3 const oldparentid = 0 const newparentid = 2 let node = state[nodeid] let oldparent = state[oldparentid] let newparent = state[newparentid] // first remove node form parent's children undoHistory.add(setState, n => n.id === node.parentid, 'children', oldparent.children.filter(n => n !== node.id)) // then add to new parent's children undoHistory.add(setState, n => n.id === newparent.id, 'children', [...newparent.children, node.id]) // lastly, point to new parent undoHistory.add(setState, n => n.id === node.id, 'parentid', newparent.id) undoHistory.unbatch() } const App: Component = () => { return ( <> <For each={state}>{(node: Node) => <div>{`id: ${node.id}, parentid: ${node.parentid}, children: ${node.children}`}</div>}</For> <button onClick={changeState}>Change Parent of 3</button> <button onClick={() => undoHistory.undo()} disabled={!undoHistory.isUndoable()}>Undo</button> <button onClick={() => undoHistory.redo()} disabled={!undoHistory.isRedoable()}>Redo</button> </> ); }; export default App;
问题分析
核心问题出在第一个操作的predicate函数n => n.id === node.parentid:
node是solid-store返回的代理对象,后续修改node.parentid的操作会改变这个对象的属性值。- solid-record在batch中记录的是函数本身,redo时函数执行会依赖当前状态下的
node.parentid值,而非操作时的原始值,导致操作目标错误指向新父节点(节点2),而非原始父节点(节点0),最终造成节点0的子节点状态更新异常。
解决方案
将第一个操作的predicate改为使用提前保存的常量oldparentid,避免依赖动态变化的node.parentid。修改后的changeState函数如下:
const changeState = () => { undoHistory.batch() const nodeid = 3 const oldparentid = 0 const newparentid = 2 let node = state[nodeid] let oldparent = state[oldparentid] let newparent = state[newparentid] // 使用oldparentid替代node.parentid,确保操作目标始终是原始父节点 undoHistory.add(setState, n => n.id === oldparentid, 'children', oldparent.children.filter(n => n !== node.id)) undoHistory.add(setState, n => n.id === newparent.id, 'children', [...newparent.children, node.id]) undoHistory.add(setState, n => n.id === node.id, 'parentid', newparent.id) undoHistory.unbatch() }
此修改确保无论node.parentid后续如何变化,第一个操作的predicate都会稳定指向原始父节点(节点0),redo时操作目标正确,状态更新符合预期。
内容的提问来源于stack exchange,提问作者Franck Dervaux
相关产品推荐
相关产品推荐

