使用 React.cloneElement 和 render prop 将 ref 传递给类组件

IT技术 reactjs ecmascript-6 ref clone-element
2021-05-09 19:47:13

我正在编写一个组件,该组件state根据ref它的子级(例如与该子级的 ref 相关的鼠标事件)处理一些内部事件。
此组件使用 arender-prop将相关部分传递state给它的子组件,并ref通过React.cloneElementutil使用附加的子组件渲染子组件

问题是,当孩子是一个class组件时,由于某种原因ref它不可用,我找不到渲染它的方法,因为它是一个类型为function(当然在我克隆它之后)的react元素对象

但是,如果孩子只是一个DOM节点div,例如,它会按预期工作。

我的解决方法是检查孩子的类型,如果它是一种类型,function我将用我自己的 包裹克隆元素div,如果它只是一个 dom 节点,则按原样呈现。
但是,我不想用额外的东西包裹孩子,div因为我不想添加不必要的DOM节点。

这是一个基本的代码示例,为简洁起见,删除了大部分代码:
父组件:

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组件)时,它不能像上面提到的那样工作。

1个回答

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

虽然警告指出:

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
        }
    }
}