使用React.cloneElement将ref传递给类组件并渲染prop

时间:2018-05-06 11:46:44

标签: reactjs ecmascript-6 ref clone-element

我正在编写一个组件来处理一些内部state根据它的孩子的ref(例如与该孩子的ref相关的鼠标事件)。
此组件使用render-propstate的相关部分传递给其子项,并通过ref util附加React.cloneElement子项。

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

但是,如果孩子只是一个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 个答案:

答案 0 :(得分:0)

我几乎遇到了相同的问题。
我选择使用react-dom中的findDOMNode,可以看到完整的解决方案in 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
        }
    }
}