Redux 怎么处理嵌套的 selector?真的整糊涂了

2022-08-18 09:48:58 +08:00
 sillydaddy

我想渲染一棵树,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 个数据,就假设它可以缓存很多个。

1742 次点击
所在节点    React
10 条回复
towry
2022-08-18 10:07:27 +08:00
你是通过 selector 选择后的数据来生成 RenderItem 树,对吗?
sillydaddy
2022-08-18 10:16:57 +08:00
@towry
selectNode 就是用来生成 RenderItem 树🌲的,它接收 store 和 node_id 这 2 个参数,会把 node_id 对应的节点以及它的子节点递归生成出一棵 RenderItem 树🌲。
BingoXuan
2022-08-18 10:47:11 +08:00
我最近工作内容也和 op 的差不多。但我并没有用扁平的方法。而是标准的树,另外保存了节点名字。通过节点名字再从树搜索节点信息。渲染时候直接渲染树,通过 memo 来缓存值每一层的子树。
kongkx
2022-08-18 11:13:23 +08:00
key 跟 object 分开处理。 再处理一下 key tree selector 的 compare 。
GreatAuk
2022-08-18 11:21:29 +08:00
接收不了 redux, 样板代码太多
sillydaddy
2022-08-18 11:39:37 +08:00
@kongkx
可以细说一下吗,感谢。
kongkx
2022-08-18 12:45:54 +08:00
@sillydaddy 做一个只包含层级关系的 key tree ( ID ) ,然后具体的节点渲染组件中通过 id ,select 对应节点的数据。 每次 state 修改的时候,还是会触发 keytree 的构建,那就通过自定义的 compare 函数来判断,前后数据是否 equal 。

memoize 的库 一般都有 equality 的方法可以设置。useSelector ,reselect 都有,具体看文档。

另外性能差异有多少,要实际测试才知道。
sillydaddy
2022-08-18 13:20:55 +08:00
@kongkx
啊,这个“compare 函数”提醒了我——
可以自定义 equality 方法,在比较参数是否相等的时候,把 store 类型的数据给忽略掉。虽然取巧,但感觉确实能达到目的。
再次感谢,虽然没太弄懂你前面说的 key tree 方法。
0xffSol
2022-08-18 18:47:45 +08:00
可以使用 useContent 、useReducer 代替 redux
ChrisV5
2022-09-28 19:02:04 +08:00
一般是 select parent as chilends 之后,再 chiledns.map(c=><div><RenderChildren/><div>)
在 RenderChidren 里面再 Select

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/873647

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX