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

react 组件重新渲染的问题

  •  
  •   oppddd · 364 天前 via iPhone · 2715 次点击
    这是一个创建于 364 天前的主题,其中的信息可能已经有所发展或是发生改变。
    有一个页面, 包含 table table 有操作选项,点击后会弹窗进行操作,关闭弹窗刷新 table

    一个很普通的页面

    问题:

    1. 如何组织弹窗的 showState ?

    在弹窗组件里面管理是否 show ,决定了应该把弹窗组件放在操作拦 每一个 cell 中,这样会生成多个弹窗实例,感觉不太好

    2. 弹窗应该放在什么位置?

    如果放在跟 table 同级,这样频繁的点击操作拦的查看按钮,会导致 table 重新渲染

    有什么最佳实践吗?
    24 条回复    2023-03-07 18:55:08 +08:00
    chairuosen
        1
    chairuosen  
       364 天前
    应该是 2 ,担心性能的话,把 table 再封一个?
    leoskey
        2
    leoskey  
       364 天前
    可以参考这篇文章《 React 组件性能优化:如何避免不必要的 re-render 》 https://mp.weixin.qq.com/s/z9GaB_48LHtL-if4mP-nZQ
    superedlimited
        3
    superedlimited  
       364 天前
    弹窗的 showState 放到 useMemo ,就不会导致 table 重新渲染了
    AyaseEri
        4
    AyaseEri  
       364 天前
    把弹窗封成一个独立组件,内部维护 showState ,对外暴露一个 toggle 接口,通过 ref 的形式让外部控制自己的显隐
    或者是像 antd 那几个提示一样直接写成一个 function ,每次直接 call function 弹开弹窗
    ChefIsAwesome
        5
    ChefIsAwesome  
       364 天前 via Android
    table 一个组件,dialog 一个组件。上面再套一层组件,相当于中间人沟通二者。
    多个 dialog 也行。你想象一下 select ,实际也是弹窗。每行来一个,没人觉得奇怪。只要 dialog 关闭的时候,把 Dom 清空就行。
    suzic
        6
    suzic  
       364 天前 via Android
    2 。会导致重新渲染么?不太懂 react ,在 vue 里边如果状态和表格无关的话,那部分是不会重新渲染的
    oppddd
        7
    oppddd  
    OP
       364 天前
    @suzic 会的,因为 vue 是将状态和副作用 render 进行了细粒度的绑定,react 不是,只要 setstate ,父组件子组件都跟着 rerender
    oppddd
        8
    oppddd  
    OP
       364 天前
    @ChefIsAwesome 再套一个组件也会 rerender ,比如我点击一个按钮,要显示用户详情,那么弹窗就需要知道是什么 userId ,那就会导致状态提升,那么三个组件都会 rerender ;
    oppddd
        9
    oppddd  
    OP
       364 天前
    @AyaseEri 嗯,感觉通过 ref 能减少 rerender ,还能减少 dialog 组件实例
    joesonw
        10
    joesonw  
       364 天前 via iPhone
    table 依赖的状态变化了才会刷新,例如有个按钮的 onClick 你直接原地手写的,每次都会变的,要用 useCallback 包起来。类似的检查检查,不会无故刷新的。
    suzic
        11
    suzic  
       364 天前 via Android
    @liuxsen93 我猜可以通过包装一个方法,每次调用时手动插入弹窗 dom ,每次用完自动销毁? vue 可以这样玩的,react 应该也可以,原理一样
    ChefIsAwesome
        12
    ChefIsAwesome  
       363 天前 via Android
    @liuxsen93 你再琢磨琢磨。ID 存父组件上,传给 dialog ,table 只是改这个 ID ,不用收这个 ID 。
    donlian
        13
    donlian  
       363 天前 via iPhone
    子组件用 React.memo 包一下,这样就不会 props 没变就触发 rerender 了
    sjhhjx0122
        14
    sjhhjx0122  
       363 天前   ❤️ 1
    推荐一下我一直在用的维护弹窗的 hook https://github.com/eBay/nice-modal-react 通过这个插件你就可以只创建一个弹窗实例,并且不维护 showState ,切用命令式调用
    dsa999
        15
    dsa999  
       363 天前
    我觉得弹出层最好放在外层,比如通过 portal 放到 body 下,这样就不会有样式污染的问题。
    不过我目前的做法是使用 ReactDom 中的 createRoot 方法,在 body 下创建一个新的根节点处理弹出层。
    优点:
    1. 不会出现你说的重新渲染问题。
    2. 不需要 showState ,通过 render/destroy 控制显隐即可。
    缺点:
    1. 由于是独立的两个根节点,会造成无法共享 context 等问题,不过可以通过 render 时把数据作为参数传入。
    yuuko
        16
    yuuko  
       363 天前
    把弹窗放在 cell 组建,显示的时候挂载弹窗,不显示销毁,就不会存在多个实例了
    Ritr
        17
    Ritr  
       363 天前
    当然是子路由啦,把弹框放到子路由里面就可以了
    monologue520
        18
    monologue520  
       363 天前
    2 , 有时候是需要 table 重复渲染的,比如说 dialog 中操作改变了 table
    sunwang
        19
    sunwang  
       363 天前
    这种情况还是倾向于在父级维护 showState ,像新增按钮这种倒是可以和弹窗封装到在一起
    sjhhjx0122
        20
    sjhhjx0122  
       363 天前
    @dsa999 其实有办法拿到上下文的,可以创建一个 ModalProvider 组件专门维护所有弹窗状态和渲染,用 context 传递状态和组件,然后拿这个组件包裹在根组件上,写一个 hook 传递 modal 给 ModalProvide 渲染,如果需要当前组件的上下文就把 ModalProvider 包在当前组件上,就能拿到上下文
    otakustay
        21
    otakustay  
       363 天前
    把触发弹窗的 button 包个组件,里面管 show 状态,这样 table 就不会渲染了。至于多个弹窗实例,用{show && <Modal />}管呗,全局遮罩层一下,不就只有一个了么
    jjwjiang
        22
    jjwjiang  
       363 天前
    在 react hooks 里不应该恐惧一般情况下产生的 re-render 。整个组件级别的 re-render 依然会被 react 优化成只对弹窗部分 re-render
    dsa999
        23
    dsa999  
       363 天前
    @sjhhjx0122 嗯,不过大多数场景下 render 时传参也够用了。弹出层从交互上来看,本来就是处理额外的独立逻辑的嘛
    oppddd
        24
    oppddd  
    OP
       358 天前
    @sjhhjx0122 看了这个仓库,用着很舒服!大佬啊
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5683 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 43ms · UTC 02:56 · PVG 10:56 · LAX 18:56 · JFK 21:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.