我正在使用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函数。
那我在这里错过了什么? :/
答案 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未记录,将来可能会更改。但是现在,这是将上下文呈现到门户组件中的正确方法。