我想渲染一棵树,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 |