我想渲染一棵树,RenderItem 是树的节点,可以不断嵌套子节点,构建好这棵树后,会把根节点 RenderItem 传给 react-sortable-tree 去渲染。
type RenderItem = {id:string, name:string, children:RenderItem[]};
这棵树的源数据,也是树状的,但是组织方式是扁平的:
store: {
"111": {name:"node1", children_ids:["222", "333"]},
"222": {name:"node2", children_ids:["444"]},
"333": {name:"node3", children_ids:["555"]},
"444": {name:"node4", children_ids:[]},
"555": {name:"node5", children_ids:[]},
}
当某些操作,修改了源数据里的一小部分,Redux 会利用 immer 生成新的 store 。
这时候如果想要渲染某个节点,就要利用 Redux 的 useSelector ,根据新的 store 生成新的 RenderItem 树🌲。
问题的关键在于,如果我只修改了某个节点的很小一部分数据,就重新生成整个 RenderItem 树🌲,会感觉效率很低。所以有没有办法使用 reselect 这种库,缓存节点的渲染数据呢?
我试了试,发现很难,比如我想到的方案
var selectNode = createSelector(
(state, node_id)=>state[node_id],
(state, node_id)=>node_id,
(node, node_id)=>{
var childrenItems = node.children_ids.map((child_id)=>selectNode(state(从哪里来?), child_id));
var item:RenderItem = {id:node_id, name:node.name, children: childrenItems};
return item;
}
)
从上面代码可以看到,selectNode 嵌套调用时,总是需要 state 这个参数的,而 state 又总是变化的,也就导致了整个树的数据都要重新生成。
不知道我说清楚了没。这里不要纠结于 reselect 默认只能缓存 1 个数据,就假设它可以缓存很多个。
1
towry 2022-08-18 10:07:27 +08:00
你是通过 selector 选择后的数据来生成 RenderItem 树,对吗?
|
2
sillydaddy OP @towry
selectNode 就是用来生成 RenderItem 树🌲的,它接收 store 和 node_id 这 2 个参数,会把 node_id 对应的节点以及它的子节点递归生成出一棵 RenderItem 树🌲。 |
3
BingoXuan 2022-08-18 10:47:11 +08:00
我最近工作内容也和 op 的差不多。但我并没有用扁平的方法。而是标准的树,另外保存了节点名字。通过节点名字再从树搜索节点信息。渲染时候直接渲染树,通过 memo 来缓存值每一层的子树。
|
4
kongkx 2022-08-18 11:13:23 +08:00 via iPhone
key 跟 object 分开处理。 再处理一下 key tree selector 的 compare 。
|
5
GreatAuk 2022-08-18 11:21:29 +08:00
接收不了 redux, 样板代码太多
|
6
sillydaddy OP @kongkx
可以细说一下吗,感谢。 |
7
kongkx 2022-08-18 12:45:54 +08:00 via iPhone
@sillydaddy 做一个只包含层级关系的 key tree ( ID ) ,然后具体的节点渲染组件中通过 id ,select 对应节点的数据。 每次 state 修改的时候,还是会触发 keytree 的构建,那就通过自定义的 compare 函数来判断,前后数据是否 equal 。
memoize 的库 一般都有 equality 的方法可以设置。useSelector ,reselect 都有,具体看文档。 另外性能差异有多少,要实际测试才知道。 |
8
sillydaddy OP @kongkx
啊,这个“compare 函数”提醒了我—— 可以自定义 equality 方法,在比较参数是否相等的时候,把 store 类型的数据给忽略掉。虽然取巧,但感觉确实能达到目的。 再次感谢,虽然没太弄懂你前面说的 key tree 方法。 |
9
0xffSol 2022-08-18 18:47:45 +08:00 via iPhone
可以使用 useContent 、useReducer 代替 redux
|
10
ChrisV5 2022-09-28 19:02:04 +08:00
一般是 select parent as chilends 之后,再 chiledns.map(c=><div><RenderChildren/><div>)
在 RenderChidren 里面再 Select |