使用React.cloneElement将ref传递给类组件并渲染prop [英] pass ref to a class component with React.cloneElement and render prop
问题描述
我正在编写一个根据其子级ref
处理某些内部state
的组件(例如,与该子级ref相关的鼠标事件).
该组件使用render-prop
将相关的state
片段传递给它的子元素,并通过React.cloneElement
util附加带有ref
的子元素.
I'm writing a component that handle some internal state
according to a ref
of it's child (a mouse event related to that child's ref for example).
This component is using a render-prop
to pass on the relevant piece of state
to it's child, and render the child with the ref
attached via React.cloneElement
util.
问题是,当子级是class
组件时,由于某些原因ref
不可用,并且由于它是一种类型为function
(当然,我克隆了之后).
The problem is that when the child is a class
component, for some reason the ref
is not available, and i can't find a way to render it as it's a react element object with a type of function
(after i clone it of course).
但是,如果子节点只是一个像div
这样的DOM
节点,则它可以正常工作.
But if the child is just a DOM
node like a div
for example, it is working as expected.
我的解决方法是检查子类型,如果它是function
类型,我将用我自己的div
包装克隆的元素,如果它只是一个dom节点,则照原样进行渲染.
但是,我不想用额外的div
包裹孩子,因为我不想添加不必要的DOM
节点.
My work-around is to check the type of the child, and if it is a type of function
I'll wrap the cloned element with my own div
, if it's just a dom node then render as is.
However, i would like to not wrap the child with an extra div
as i don't want to add unnecessary DOM
nodes.
这是一个基本的代码示例,为简洁起见,删除了大多数代码:
父级组件:
Here is a basic code example, most code removed for brevity:
The Parent component:
class Parent extends Component {
attachRef = node => {
this.ref = node;
}
render() {
const { render } = this.props;
const { someValue } = this.state;
const Child = render(someValue);
const WithRef = React.cloneElement(Child, {
ref: this.attachRef
});
if (typeof WithRef.type === 'string') { // node element
return WithRef;
}
else if (typeof WithRef.type === 'function') {
// this is a react element object.. not sure how to render it
// return ?
} else {
// need to find a way to render without a wrapping div
return (
<div ref={this.attachRef}>{Child}</div>
);
}
}
}
用法:
class App extends Component {
render() {
return (
<div>
<Parent render={someValue => <div> {someValue}</div>} />
<Parent render={someValue => <Menu someValue={someValue} />} />
</div>
);
}
}
当我像第一个示例一样渲染常规DOM节点时,它工作正常,当我尝试渲染Menu
(这是一个class
组件)时,它如上所述无法工作.
When i render regular DOM nodes like the first example it works fine, when i try to render the Menu
(which is a class
component) it doesn't work as mentioned above.
推荐答案
我几乎遇到了相同的问题.
我选择使用react-dom
中的 findDOMNode ,您可以看到完整的解决方案在react-external-click中 .
I had almost an identical issue.
i chose to use findDOMNode from react-dom
, you can see the full solution in react-external-click.
尽管警告提示:
findDOMNode是用于访问基础DOM节点的转义口. 在大多数情况下,不建议使用此逃生舱口,因为它会 破坏了组件的抽象.
findDOMNode is an escape hatch used to access the underlying DOM node. In most cases, use of this escape hatch is discouraged because it pierces the component abstraction.
findDOMNode仅适用于已安装的组件(即, 已放置在DOM中).如果您尝试在组件上调用它 尚未挂载的对象(例如在render()中调用findDOMNode() 在尚未创建的组件上)将出现异常 抛出.
findDOMNode only works on mounted components (that is, components that have been placed in the DOM). If you try to call this on a component that has not been mounted yet (like calling findDOMNode() in render() on a component that has yet to be created) an exception will be thrown.
findDOMNode不能在功能组件上使用.
findDOMNode cannot be used on functional components.
我认为这是解决此特定挑战的更好解决方案.
它使您对使用者透明",同时能够定位DOM
中的组件.
I think this is the better solution for this particular challenge.
It let's you be "transparent" to the consumer, while being able to target the component in the DOM
.
好,就在这里,拿起裁判:
Ok here it is, grabbing the ref:
componentDidMount() {
this.ref = findDOMNode(this);
// some logic ...
}
这是我使用不带包装的渲染函数的方式:
this is how i use a render function with no wrapper of my own:
render() {
const { children, render } = this.props;
const { clickedOutside } = this.state;
const renderingFunc = render || children;
if (typeof renderingFunc === 'function') {
return renderingFunc(clickedOutside);
} else {
return null
}
}
}
这篇关于使用React.cloneElement将ref传递给类组件并渲染prop的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!