Redo functionality for entity records does not set status correctly
P粉029057928
P粉029057928 2023-09-15 19:27:41
0
1
806

I'm building a mind mapping application using entity records. The data in the application is a tree of nodes and I want to be able to reparent the nodes via undo/redo. The problem I'm facing is that even though the undo operation works fine, the redo operation does not.

I recreated my problem in a simple application:

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;

When the "Change Parent of 3" button is clicked, changeState function:

  1. Remove node 3 from its parent (node ​​0) child list
  2. Add node 3 to its new parent node (node ​​2) child node list
  3. Reset the parent node of node 3 to 2

The redo correctly restores the state to its initial value, with columns 1,2,3 being children of 0.

But Redo sets the child node list of node 0 to 3, when it should set it to 1,2! Very strangely, this doesn't happen if I don't set the Parentis property of node 3, which is not directly related to the Children property of node 0...

I suspect this is a reference vs. value type issue, or maybe a bug in the entity record... Any help?

P粉029057928
P粉029057928

reply all(1)
P粉729518806

As I suspected, the question is a reference question... When performing the first operation, use the "search" function n => n.id === node.parentid to select the correct element of the array. However, the way solid-record stores command history is such that it only stores "node" objects. And the parentid property of the same object is modified by the last operation. The problem was solved by using a local variable that stores node.parentid.

Alternatively, after reading the SolidJS store API documentation better, use the id directly instead of the search function, for example:

undoHistory.add(setState, node.parentid, 'children', oldparent.children.filter(n => n !== node.id))
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template