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

2022-01-28 11:46:54 +08:00
 yazoox

最新在使用 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 元素呢?

谢谢!

1825 次点击
所在节点    React
11 条回复
GentleFifth
2022-01-28 11:50:18 +08:00
forwardRef
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
2022-01-28 12:08:39 +08:00
@momocraft 但这个 FC 是第三方的组件,不是我们自己的。这个就得去改他们的源代码了。可能比较难。

@GentleFifth 能多说两句么?
Yukee798
2022-01-28 12:12:27 +08:00
React.forwardRef 应该能解决你的问题,可以去看一下文档 https://react.docschina.org/docs/forwarding-refs.html
GentleFifth
2022-01-28 12:13:22 +08:00
@yazoox 2 楼是对的,如果 FC 是第三方组件的话,建议外面包一层 div ,给 react-dnd 使用,forwardRef 可以给 FC 透传 ref
momocraft
2022-01-28 12:31:33 +08:00
如果这个 FC 无法以任何方式塞自己的 DOM element 进去 (包括 children 和各种 props) , 就套一层 div 好了
shilianmlxg
2022-01-28 13:52:38 +08:00
听大佬们分析 学了好多东西
66beta
2022-01-28 13:53:52 +08:00
你这组件没有 forwardRef 吧
yazoox
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
2022-02-11 11:15:38 +08:00
@yazoox 可以 props 一层层传下去,为什么要在外层操作遥远的里层?
KuroNekoFan
2022-02-11 17:34:02 +08:00
一层一层传感觉好痛苦,这种我选择 context....

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

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

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

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

© 2021 V2EX