我正在尝试使用React Router渲染打印页面。所以我有两个组成部分:
export default class PurchaseOrder extends React.Component{
....
render(){
const {orderDate, client} = this.state.order;
//omitted for brevity
return(
<BrowserRoute>
<Button
component={Link}
to="/order/print"
target="_blank"
>
Print
</Button>
<Route
path="/order/print"
render={props => (
<OrderPrint
{...props}
orderDate={orderDate}
client={client}
/>
)}
/>
</BrowserRoute>
}
}
和OrderPrint:
export default function OrderPrint(props) {
return (
<div>props.orderDate</div>
<div>props.client.name</div>
);
}
如您所见,我正在尝试通过单击按钮来显示采购订单的可打印版本。 OrderPrint组件被渲染,但是它在按钮正下方渲染。我可以将Route放在我的根组件(即App)中,以确保仅得到如下所示呈现的OrderPrint组件的内容:
class App extends Component {
render() {
return (
<div className="App">
<Router>
<Route exact path="/" component={PurchaseOrder} />
<Route exact path="/order/print" component={OrderPrint} />
</Router>
</div>
);
}
}
但是在那种情况下,我将无法传递必要的道具。因此,在这种特殊情况下,如何用OrderPrint组件的内容替换整个页面的内容,并且仍然能够将必要的输入传递给它?
正如@Akalanka Weerasooriya在评论中提到的那样,我可以将整个状态保留在App组件中。但是有一件事阻止了我这样做:这意味着我几乎总是必须使用Route组件的render
道具,而不是component
道具。好的,这不是问题,但是如果这是可行的方法,那么为什么React Router文档几乎总是使用
<Route path="/about" component={About} />
模式作为标准使用方式吗?因此,总而言之,如果我采用“真理的唯一来源”并将所有状态存储在一个地方,那么这并不意味着我将始终使用
<Route path="/about" render={props=>(<div>props.someProp</div>)} />
我不是说这有问题,只是在文档中提到component={SomeComponent}
模式使我感到困惑之后。
答案 0 :(得分:1)
不确定为什么要为打印页面使用其他路由,但是无论如何,如果您希望在新的空白页面上使用它,则可以利用ReactDOM.createPortal功能。
您可以使用window.open
创建新页面,甚至创建新窗口,同时保持反应数据流同步。
这是新窗口上的门户网站的运行示例,其中使用门户网站触发该窗口的组件的实时状态更新:
running example,我在这里共享一个外部代码段,而不使用堆栈代码段,因为window.open
在堆栈代码段的上下文中返回null
源代码:
class WindowPortal extends React.PureComponent {
containerEl = document.createElement("div");
externalWindow = null;
componentDidMount() {
const { width = 450, height = 250, left = 150, top = 150 } = this.props;
const windowFetures = `width=${width},height=${height},left=${left},top=${top}`;
this.externalWindow = window.open("", "", windowFetures);
this.externalWindow.document.body.appendChild(this.containerEl);
}
componentWillUnmount() {
this.externalWindow.close();
}
render() {
return ReactDOM.createPortal(this.props.children, this.containerEl);
}
}
class App extends React.PureComponent {
state = {
counter: 0,
showWindowPortal: false
};
componentDidMount() {
window.setInterval(() => {
this.setState(state => ({
counter: state.counter + 1
}));
}, 1000);
}
toggleWindowPortal = () => {
this.setState(state => ({
...state,
showWindowPortal: !state.showWindowPortal
}));
};
closeWindowPortal = () => {
this.setState({ showWindowPortal: false });
};
render() {
return (
<div>
<h1>Counter: {this.state.counter}</h1>
<button onClick={this.toggleWindowPortal}>
{this.state.showWindowPortal ? "Close the" : "Open a"} Portal
</button>
{this.state.showWindowPortal && (
<WindowPortal closeWindowPortal={this.closeWindowPortal}>
<h2>We are in a portal on a new window</h2>
<h3>{`This is the current state: ${this.state.counter}`}</h3>
<p>different window but sharing the state!!</p>
<button onClick={() => this.closeWindowPortal()}>Close me!</button>
</WindowPortal>
)}
</div>
);
}
}
答案 1 :(得分:1)
这里有一个PrivateRoute
,这是一个自定义路由,其中包含标头,并且标头仅在PrivateRoute
路由中呈现,因此当您尝试导航至path="/order/print"
之类的新路由时,将不会获得带有按钮的标题。
function Header(props) {
return (
<div>
<Button
component={Link}
to="/order/print"
target="_blank">
Print</Button>
{props.children}
</div>
)
}
const PrivateRoute = ({ component: Component, layout: Layout, ...rest }) => {
return <Route {...rest} render={props => {
return <Layout>
<Component {...props} />
</Layout>
}} />
}
export default class PurchaseOrder extends React.Component{
render(){
const {orderDate, client} = this.state.order;
//omitted for brevity
return(
<BrowserRouter>
<div>
<PrivateRoute exact path="/" layout={Header} component={Landing} />
<Route
path="/order/print"
render={props => (
<OrderPrint
{...props}
orderDate={orderDate}
client={client}
/>
)}
/>
</div>
</BrowserRouter>
}
}