V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
sillydaddy
V2EX  ›  React

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

  •  
  •   sillydaddy · 2022-08-18 09:48:58 +08:00 · 1943 次点击
    这是一个创建于 833 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    10 条回复    2022-09-28 19:02:04 +08:00
    towry
        1
    towry  
       2022-08-18 10:07:27 +08:00
    你是通过 selector 选择后的数据来生成 RenderItem 树,对吗?
    sillydaddy
        2
    sillydaddy  
    OP
       2022-08-18 10:16:57 +08:00
    @towry
    selectNode 就是用来生成 RenderItem 树🌲的,它接收 store 和 node_id 这 2 个参数,会把 node_id 对应的节点以及它的子节点递归生成出一棵 RenderItem 树🌲。
    BingoXuan
        3
    BingoXuan  
       2022-08-18 10:47:11 +08:00
    我最近工作内容也和 op 的差不多。但我并没有用扁平的方法。而是标准的树,另外保存了节点名字。通过节点名字再从树搜索节点信息。渲染时候直接渲染树,通过 memo 来缓存值每一层的子树。
    kongkx
        4
    kongkx  
       2022-08-18 11:13:23 +08:00 via iPhone
    key 跟 object 分开处理。 再处理一下 key tree selector 的 compare 。
    GreatAuk
        5
    GreatAuk  
       2022-08-18 11:21:29 +08:00
    接收不了 redux, 样板代码太多
    sillydaddy
        6
    sillydaddy  
    OP
       2022-08-18 11:39:37 +08:00
    @kongkx
    可以细说一下吗,感谢。
    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 都有,具体看文档。

    另外性能差异有多少,要实际测试才知道。
    sillydaddy
        8
    sillydaddy  
    OP
       2022-08-18 13:20:55 +08:00
    @kongkx
    啊,这个“compare 函数”提醒了我——
    可以自定义 equality 方法,在比较参数是否相等的时候,把 store 类型的数据给忽略掉。虽然取巧,但感觉确实能达到目的。
    再次感谢,虽然没太弄懂你前面说的 key tree 方法。
    0xffSol
        9
    0xffSol  
       2022-08-18 18:47:45 +08:00 via iPhone
    可以使用 useContent 、useReducer 代替 redux
    ChrisV5
        10
    ChrisV5  
       2022-09-28 19:02:04 +08:00
    一般是 select parent as chilends 之后,再 chiledns.map(c=><div><RenderChildren/><div>)
    在 RenderChidren 里面再 Select
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5404 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 07:51 · PVG 15:51 · LAX 23:51 · JFK 02:51
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.