使用组件作为prop

时间:2015-05-21 08:31:10

标签: reactjs react-bootstrap

我正在使用react-bootstrap的ModalTrigger来显示一个字段重的模态(基于react-bootstrap的模态),这意味着向它发送一堆道具:

<ModalTrigger modal={<MyModal field1={value1} field2={value2} (more fields...)/>}>
    Click here to open
</ModalTrigger>

创建触发器的父组件具有通过props传入的字段/值,的父组件组件也将其作为道具传递,实际上是顶层组件保存数据。两者基本上都是管道,这是一个经典的childContext场景,除了它不起作用。这是我尝试过的简化版本:

var MyModal = React.createClass({
    contextTypes : {foo : React.PropTypes.string},
    render : function() {
        return (
            <Modal {...this.props} title="MyTitle">
                <div className="modal-body">
                    The context is {this.context.foo}
                </div>
            </Modal>
        );
    }
});

var Content = React.createClass({
    childContextTypes : {foo: React.PropTypes.string},
    getChildContext : function() {return {foo : "bar"}},
    render : function() {
        return (
            <ModalTrigger modal={<MyModal/>}>
                <span>Show modal</span>
            </ModalTrigger>
        )
    }
});

模式弹出“上下文是”,没有显示实际上下文。

我相信这种情况正在发生,因为发送给ModalTrigger的道具已经以某种方式渲染/装载,但我不确定为什么。据我所知,MyModal的所有者是内容组件,这意味着上下文应该没问题,但事实并非如此。

更多信息:我已经尝试将{...this.props}context={this.context}传递给MyModal但没有成功。另外,也许相关,ModalTrigger使用cloneElement来确保模态的onRequestHide prop指向触发器的hide函数。

那我在这里错过了什么? :/

2 个答案:

答案 0 :(得分:10)

True道具被覆盖时,

React.cloneElement将更改元素的所有者,这意味着不会从前一个所有者传递上下文。 但是,ref似乎并非如此。

请注意,基于所有者的方法在React 0.14中完全不起作用,因为上下文将从父节点传递到子节点而不是从所有者传递到ownee。 ModalTrigger在DOM的另一个分支中呈现其ModalTrigger节点prop(请参阅OverlayMixin)。因此,您的modal组件不是您的Modal组件的子级或后代,也不会从Content传递子级上下文。

至于解决问题,你总是可以创建一个组件,其唯一目的是将上下文传递给子组件。

Content

使用它:

var PassContext = React.createClass({
  childContextTypes: {
    foo: React.PropTypes.string
  },

  getChildContext: function() {
    return this.props.context;
  },

  render: function() {
    return <MyModal />;
  },
});

Matt Smith暗示,事实证明,react-bootstrap已经包含了一种非常类似于通过ModalTrigger.withContext转发上下文的方法。这允许您创建一个<ModalTrigger modal={<PassContext context={this.getChildContext()}/>}> 组件类,它将其上下文转发到其ModalTrigger节点prop,无论它在VDOM树中的位置。

modal

答案 1 :(得分:3)

有一种更好的方法可以将上下文传递给您的“门户”类型组件,这些组件将其子项呈现在React树之外的不同容器中。

使用“renderSubtreeIntoContainer”而不是“render”也会将上下文传递给子树。

可以像这样使用:

import React, {PropTypes} from 'react';
import {
    unstable_renderSubtreeIntoContainer as renderSubtreeIntoContainer,
    unmountComponentAtNode
} from 'react-dom';

export default class extends React.Component {
  static displayName = 'ReactPortal';

  static propTypes = {
    isRendered: PropTypes.bool,
    children: PropTypes.node,
    portalContainer: PropTypes.node
  };

  static defaultProps = {
    isRendered: true
  };

  state = {
    mountNode: null
  };

  componentDidMount() {
    if (this.props.isRendered) {
      this._renderPortal();
    }
  }

  componentDidUpdate(prevProps) {
    if (prevProps.isRendered && !this.props.isRendered ||
      (prevProps.portalContainer !== this.props.portalContainer &&
         prevProps.isRendered)) {
      this._unrenderPortal();
    }

    if (this.props.isRendered) {
      this._renderPortal();
    }
  }

  componentWillUnmount() {
    this._unrenderPortal();
  }

  _getMountNode = () => {
    if (!this.state.mountNode) {
      const portalContainer = this.props.portalContainer || document.body;
      const mountNode = document.createElement('div');
      portalContainer.appendChild(mountNode);
      this.setState({
        mountNode
      });

      return mountNode;
    }

    return this.state.mountNode;
  };

  _renderPortal = () => {
    const mountNode = this._getMountNode();
    renderSubtreeIntoContainer(
      this,
      (
        <div>
          {this.props.children}
        </div>
      ),
      mountNode,
    );
  };

  _unrenderPortal = () => {
    if (this.state.mountNode) {
      unmountComponentAtNode(this.state.mountNode);
      this.state.mountNode.parentElement.removeChild(this.state.mountNode);
      this.setState({
        mountNode: null
      });
    }
  };

  render() {
    return null;
  }
};

这是我在production app Casalova中使用的门户网站示例,它将上下文正确地呈现给他们的孩子。

注意:此API未记录,将来可能会更改。但是现在,这是将上下文呈现到门户组件中的正确方法。