我有像这样一个复杂的,动态的数据结构:
const tree = [
{
name: "Root Node",
collapsed: true,
nodes: [
{
name: "Node 1",
collapsed: true,
nodes: [
{
name: "Sub node"
}
]
},
{
name: "Node 2",
collapsed: true,
nodes: [
{
name: "Sub node "
}
]
},
{
name: "Node 3",
collapsed: true,
nodes: [
{
name: "Sub node"
}
]
}
]
}
];`
我设置这个作为我的组件的初始状态。
我则呈现这种状态出作为UI中的层次树。
当我点击顶级节点我想更新状态倒塌属性把它打开并显示下一组节点。
我遇到的问题是,我该怎么称呼的setState()和无致病突变,并做一些丑陋的东西,像树[0] .nodes [0] .collapsed更新这个复杂的数据结构:假的。
所以我第一次尝试设置状态这样
handleClick(el, event) {
this.setState({
tree: this.findAndUpdateState(event.target.id).bind(this)
});
}
所以,在我把这个节点这就要求findAndUpdateState的handleClick事件。
findAndUpdateState(id) {
this.state.tree.map((node) => {
//Map over the nodes somehow and find the node that needs its state updated using the ID?
});
});
}
我想更新已试图扩大节点的折叠性能。但这样做我经历了所有国家必须循环,然后创建状态的新副本只是为了更新一个属性。必须有,我没有想到一个更好和更简单的方法。
反应在做这些样的东西很不错。
我用的钩子反应,因为我喜欢他们.. :)
这里是一个工作片断如下..
const tree = [{"name":"Root Node","collapsed":true,"nodes":[{"name":"Node 1","collapsed":true,"nodes":[{"name":"Sub node"}]},{"name":"Node 2","collapsed":true,"nodes":[{"name":"Sub node "}]},{"name":"Node 3","collapsed":true,"nodes":[{"name":"Sub node"}]}]}];
const {useState} = React;
function TreeItem(props) {
const {item} = props;
const [collapsed, setCollapsed] = useState(item.collapsed);
return <div className="item">
<span onClick={() => setCollapsed(!collapsed)}>{item.name}</span>
{!collapsed && item.nodes &&
<div style={{paddingLeft: "1rem"}}>
<TreeList list={item.nodes}/>
</div>
}
</div>
}
function TreeList(props) {
const {list} = props;
return <div>{list.map(f => <TreeItem key={f.name} item={f}/>)}</div>;
}
ReactDOM.render(<TreeList list={tree}/>, document.querySelector('#mount'));
.item {
cursor: pointer;
user-select: none;
}
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="mount"></div>
你可以创建自己的状态Node
组件collapsed
:
class Node extends Component {
state = {
collapsed: true,
}
toggle = () => {
this.setState(prevState => ({ collapsed: !prevState.collapsed }))
}
render() {
return (
<div>
<p onClick={this.toggle}>{this.props.node.name}</p>
{!this.state.collapsed && (
<div>{this.props.children}</div>
)}
</div>
)
}
}
并创建一个父组件Tree
是递归解析所有的节点:
const TREE = [{ name: "Root Node", nodes: [...] }]
class Tree extends Component {
renderNodesRecursively = parent => {
return (
<Node node={parent} key={parent.name}>
{parent.nodes
? parent.nodes.map(node => this.renderNodesRecursively(node))
: null
}
</Node>
)
}
render() {
return TREE.map(node => this.renderNodesRecursively(node))
}
}
你会发现,孩子的状态,每次切换父时间损失。它的“正常”,因为切换父挂载/卸载的孩子们。如果你想避免这种情况,你可以将这段代码
{!this.state.collapsed && (
<div>{this.props.children}</div>
)}
同
<div style={{ display: this.state.collapsed ? 'none' : 'block' }}>
{this.props.children}
</div>