我有一个树组件,它接受以下数据结构
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) })
我们的高阶函数可以专门用于编写activate
和deactivate
-
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))