如何有效地更改JS/TS中具有递归的对象中某个键的值



我有一个树组件,它接受以下数据结构

type TreeNode = {
id: string,
parentId: string,
renderer: () => React.ReactNode,
expanded: boolean,
children?: Array<TreeNode>,
}

我想实现Expand All / Collapse All功能,从技术上讲,它将该树的所有树节点的expanded更改为true/false

当我使用label: string而不是renderer: function时,我使用了以下方法,这似乎是完美的。

function updateAllNodes(rootNode, newExpanded) {
return JSON.parse(
JSON.stringify(rootNode)
.replaceAll(
`"expanded": ${!newExpanded}`, 
`"expanded": ${newExpanded}`
)
);
}

但只要我们有一个节点的渲染器函数,我就不能再使用这种方法了。

对此,最好的解决方案是什么?

假设我们有mytree-

const mytree =
{ id: 1, active: false, children: [
{ id: 11, active: true, children: [
{ id: 111, active: false },
{ id: 112, active: true }
]
},
{ id: 12, active: false },
{ id: 13, active: true, children: [
{ id: 131, active: false }
]
}
]
}

您可以使用递归函数recUpdate-免疫更新

const recUpdate = (t, f) =>
({ ...t, children: t?.children?.map(c => recUpdate(c, f)), ...f(t) })

我们的高阶函数可以专门用于编写activatedeactivate-

const activate = t =>
recUpdate(t, _ => ({ active: true }))
const deactivate = t =>
recUpdate(t, _ => ({ active: false }))

调用activate将生成一个新的树,其中所有active = true-

console.log(activate(mytree))
{ id: 1, active: true, children: [
{ id: 11, active: true, children: [
{ id: 111, active: true },
{ id: 112, active: true }
]
},
{ id: 12, active: true },
{ id: 13, active: true, children: [
{ id: 131, active: true }
]
}
]
}

调用deactivate将生成一个新的树,其中所有active = false-

console.log(deactivate(mytree))
{ id: 1, active: false, children: [
{ id: 11, active: false, children: [
{ id: 111, active: false },
{ id: 112, active: false }
]
},
{ id: 12, active: false },
{ id: 13, active: false, children: [
{ id: 131, active: false }
]
}
]
}

mytree未突变-

console.log(mytree)
{ id: 1, active: false, children: [
{ id: 11, active: true, children: [
{ id: 111, active: false },
{ id: 112, active: true }
]
},
{ id: 12, active: false },
{ id: 13, active: true, children: [
{ id: 131, active: false }
]
}
]
}

展开下面的代码段,在浏览器中验证结果-

const recUpdate = (t, f) =>
({ ...t, children: t?.children?.map(c => recUpdate(c, f)), ...f(t) })
const activate = t =>
recUpdate(t, _ => ({ active: true }))
const deactivate = t =>
recUpdate(t, _ => ({ active: false }))
const mytree =
{ id: 1, active: false, children: [
{ id: 11, active: true, children: [
{ id: 111, active: false },
{ id: 112, active: true }
]
},
{ id: 12, active: false },
{ id: 13, active: true, children: [
{ id: 131, active: false }
]
}
]
}
console.log(JSON.stringify(activate(mytree)))
console.log(JSON.stringify(deactivate(mytree)))
console.log(JSON.stringify(mytree))

最新更新