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

如何给一个函数组件挂载 ref 上去?

  •  
  •   yazoox · 2022-01-28 11:46:54 +08:00 · 1961 次点击
    这是一个创建于 1007 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最新在使用 react-dnd ,如下例子,useDrag 会返回一个 drag ,这个需要挂载到一个组件的 ref 上去 如果是一个 html element 或者 class component ,是有 ref 的,但是一个函数组件,是没有 ref 的,怎么挂上去呢?

    export const Box = function Box({ name }) {
      const [{ isDragging }, drag] = useDrag(() => ({
        type: ItemTypes.BOX,
        item: { name },
        end: (item, monitor) => {
          const dropResult = monitor.getDropResult();
          if (item && dropResult) {
            window.alert(`You dropped ${item.name} into ${dropResult.name}!`);
          }
        },
        collect: monitor => ({
          isDragging: monitor.isDragging(),
          handlerId: monitor.getHandlerId()
        })
      }));
      const opacity = isDragging ? 0.4 : 1;
      return (
        <div
          ref={drag}
          style={{ ...styleBox, opacity }}
          data-testid={`box-${name}`}
        >
          {name}
        </div>
      );
    };
    

    如果是个函数组件,比如,div 换成一个 function component ,drag 无法生效。 debug into react-dnd 的源代码,就会发现,找不到 drag source,因为 ref 一直是 null

    export const Box = function Box({ name }) {
      const [{ isDragging }, drag] = useDrag(() => ({
        type: ItemTypes.BOX,
        item: { name },
        end: (item, monitor) => {
          const dropResult = monitor.getDropResult();
          if (item && dropResult) {
            window.alert(`You dropped ${item.name} into ${dropResult.name}!`);
          }
        },
        collect: monitor => ({
          isDragging: monitor.isDragging(),
          handlerId: monitor.getHandlerId()
        })
      }));
      const opacity = isDragging ? 0.4 : 1;
      return (
        <FunctionComponent
          ref={drag}
          style={{ ...styleBox, opacity }}
          data-testid={`box-${name}`}
        >
          {name}
        </FunctionComponent>
      );
    };
    
    

    当然,这个 FunctionComponent 也是一个复杂组件,可能级联下去,孙子的孙子也是一个 div 元素。 但是怎么能够把 react-dnd 的的这个 drag ,传递给孙子的孙子的 div 元素 ref 元素呢?

    谢谢!

    11 条回复    2022-02-11 17:34:02 +08:00
    GentleFifth
        1
    GentleFifth  
       2022-01-28 11:50:18 +08:00 via Android
    forwardRef
    momocraft
        2
    momocraft  
       2022-01-28 11:58:10 +08:00
    这个 ref 应该设计目的是给 DOM element 的 (得到一个 class component 的 instance 没意义, 因为不知道把 event handler 挂到 DOM 的哪里去)

    因此不管是 class comopnent 还是 FC, 你都可以通过一个不叫 ref 的 prop 传递 只要最终传到一个 DOM element 就行
    yazoox
        3
    yazoox  
    OP
       2022-01-28 12:08:39 +08:00
    @momocraft 但这个 FC 是第三方的组件,不是我们自己的。这个就得去改他们的源代码了。可能比较难。

    @GentleFifth 能多说两句么?
    Yukee798
        4
    Yukee798  
       2022-01-28 12:12:27 +08:00
    React.forwardRef 应该能解决你的问题,可以去看一下文档 https://react.docschina.org/docs/forwarding-refs.html
    GentleFifth
        5
    GentleFifth  
       2022-01-28 12:13:22 +08:00 via Android
    @yazoox 2 楼是对的,如果 FC 是第三方组件的话,建议外面包一层 div ,给 react-dnd 使用,forwardRef 可以给 FC 透传 ref
    momocraft
        6
    momocraft  
       2022-01-28 12:31:33 +08:00
    如果这个 FC 无法以任何方式塞自己的 DOM element 进去 (包括 children 和各种 props) , 就套一层 div 好了
    shilianmlxg
        7
    shilianmlxg  
       2022-01-28 13:52:38 +08:00
    听大佬们分析 学了好多东西
    66beta
        8
    66beta  
       2022-01-28 13:53:52 +08:00
    你这组件没有 forwardRef 吧
    yazoox
        9
    yazoox  
    OP
       2022-02-11 10:22:28 +08:00
    @momocraft 谢谢。
    “通过一个不叫 ref 的 prop 传递" 是可以的,我已经尝试过了,可以工作。
    再请教一下,
    如果一个 FC ,层级下去,都是 FC ,只到最后一层才是一个 DOM Element 、HTML element ,那是不是每一层的 FC ,都需要使用 forwardRef 包装一下,把 ref 赋值给下一层的 ref prop ?


    @66beta 没有。但这个组件有 N 层,只在第一层 forwardRef ,不够吧?是不是得每层都 forwardRef 一下?
    66beta
        10
    66beta  
       2022-02-11 11:15:38 +08:00
    @yazoox 可以 props 一层层传下去,为什么要在外层操作遥远的里层?
    KuroNekoFan
        11
    KuroNekoFan  
       2022-02-11 17:34:02 +08:00
    一层一层传感觉好痛苦,这种我选择 context....
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2240 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 00:01 · PVG 08:01 · LAX 17:01 · JFK 20:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.