7

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

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

创建触发器的父组件具有通过 props 传入的字段/值,并且该组件的父组件通过实际保存数据的顶级组件将其作为 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 的所有者是 Content 组件,这意味着上下文应该没问题,但事实并非如此。

更多信息:我已经尝试过传递{...this.props}并传递context={this.context}给 MyModal,但没有成功。此外,可能相关的是,ModalTrigger 使用 cloneElement 来确保模式的 onRequestHide 道具指向触发器的隐藏功能。

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

4

2 回答 2

10

React.cloneElement将在refprop 被覆盖时更改元素的所有者,这意味着不会从先前的所有者传递上下文。但是,情况似乎并非如此ModalTrigger

请注意,基于所有者的方法在 React 0.14 中不会完全起作用,因为上下文将从父级传递给子级,而不是从所有者到拥有者。在 DOM 的另一个分支中ModalTrigger呈现其节点道具(请参阅 参考资料)。因此,您的组件既不是子组件也不是组件的后代,也不会从.modalOverlayMixinModalContentContent

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

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

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

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

要使用它:

<ModalTrigger modal={<PassContext context={this.getChildContext()}/>}>

正如马特史密斯暗示的那样,react-bootstrap 已经包含了一种非常相似的方法来通过ModalTrigger.withContext. 这允许您创建一个ModalTrigger组件类,它将其上下文转发到其modal节点道具,无论其在 VDOM 树中的位置如何。

// MyModalTrigger.js
module.exports = ModalTrigger.withContext({
  foo: React.PropTypes.String
});
于 2015-05-21T11:36:44.013 回答
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;
  }
};

这是我在生产应用程序 Casalova中使用的一个门户示例,它可以将上下文正确地呈现给他们的孩子。

注意:此 API 未记录在案,将来可能会更改。不过,就目前而言,将上下文渲染到门户组件中是正确的方法。

于 2016-03-07T05:55:12.230 回答