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.
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).
But if the child is just a DOM
node like a div
for example, it is working as expected.
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>
);
}
}
}
The usage:
class App extends Component {
render() {
return (
<div>
<Parent render={someValue => <div> {someValue}</div>} />
<Parent render={someValue => <Menu someValue={someValue} />} />
</div>
);
}
}
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.
I had almost an identical issue.
i chose to use findDOMNode from react-dom
, you can see the full solution in react-external-click.
Although the warning notes:
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 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 cannot be used on functional components.
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
}
}
}
来源:https://stackoverflow.com/questions/50199328/pass-ref-to-a-class-component-with-react-cloneelement-and-render-prop