如何在全新的空白页面中呈现React Route组件

时间:2019-06-02 09:22:19

标签: javascript reactjs react-router

我正在尝试使用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}模式使我感到困惑之后。

2 个答案:

答案 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>

   }
}