首页 > 解决方案 > pass ref to a class component with React.cloneElement and render prop

问题描述

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.

标签: reactjsecmascript-6refclone-element

解决方案


我有几乎相同的问题。
我选择使用findDOMNode,你可以在 react-external-click 中react-dom看到完整的解决方案。

虽然警告说明:

findDOMNode 是一个用于访问底层 DOM 节点的逃生舱口。在大多数情况下,不鼓励使用此逃生舱口,因为它会穿透组件抽象。

findDOMNode 仅适用于已安装的组件(即已放置在 DOM 中的组件)。如果您尝试在尚未安装的组件上调用它(例如在尚未创建的组件上调用 render() 中的 findDOMNode()),则会引发异常。

findDOMNode 不能用于功能组件。

我认为这是应对这一特殊挑战的更好解决方案。
它让您对消费者“透明”,同时能够针对DOM.

好的,在这里,抓住参考:

componentDidMount() {
    this.ref = findDOMNode(this);
    // some logic ...
}

这就是我使用没有自己的包装器的渲染函数的方式:

render() {
        const { children, render } = this.props;
        const { clickedOutside } = this.state;
        const renderingFunc = render || children;

        if (typeof renderingFunc === 'function') {
            return renderingFunc(clickedOutside);
        } else {
            return null
        }
    }
}

推荐阅读