React路由器仅模式路由



我的应用程序中确实有一个公共和私人区域,我希望能够在公共视图中的任何地方显示登录模式对话框。模态应该有自己的路线。第二个用例是私人区域中的概要模式。

问题是,当显示模态时,背景中的当前视图将消失,因为模态不是当前视图路线的子级。

由于我不想在每个可能的视图路由中添加相同的模态,所以问题是:是否可以将模态路由与其父路由解耦,并在应用程序中到处显示它们,而不需要主要内容渲染?最好的方法是什么?我发现了这个问题,但似乎不是同一个问题。

模式路由上的浏览器刷新不会在后台显示任何内容,但这是一个我可以接受的问题。

我认为您的最佳选择可能是使用隐藏的state或查询字符串(用于永久链接),或两者兼有,尤其是在任何页面上都可以显示模态(例如登录模态)的情况下。为了防止您不知道,React Router公开了历史API的state部分,允许您将数据存储在用户的历史中,而这些数据实际上在URL中不可见。

以下是我脑海中的一组路线;如果需要,可以直接跳到JSBin上的工作示例中。您还可以在自己的窗口中查看生成的示例应用程序,这样您就可以看到URL的变化(为了与JSBin兼容,它使用了哈希位置策略),并确保刷新如您所期望的那样工作。

const router = (
  <Router>
    <Route component={LoginRedirect}>
      <Route component={LocationDisplay}>
        <Route path="/" component={ModalCheck}>
          <Route path="/login" component={makeComponent("login")} />
          <Route path="/one" component={makeComponent("one")} />
          <Route path="/two" component={makeComponent("two")} />
          <Route path="/users" component={makeComponent("users")}>
            <Route path=":userId" component={UserProfileComponent} />
          </Route>
        </Route>
      </Route>
    </Route>
  </Router>
);

让我们研究一下这些路线及其组成部分。

首先,makeComponent只是一个方法,它接受一个字符串并创建一个React组件,该组件将该字符串渲染为标头,然后渲染其所有子项;这只是创建组件的一种快速方法。

LoginRedirect是一个有一个目的的组件:检查路径是否为/login,或者当前路径上是否有?login查询字符串。如果这两者中的任何一个为true,当前状态不包含login密钥,则会将状态上的login密钥设置为true。如果任何子路由匹配(即,始终渲染组件),则使用该路由。

class LoginRedirect extends React.Component {
  componentWillMount() {
    this.handleLoginRedirect(this.props);
  }
  componentWillReceiveProps(nextProps) {
    this.handleLoginRedirect(nextProps);
  }
  handleLoginRedirect(props) {
    const { location } = props;
    const state = location.state || {};
    const onLoginRoute = location.query.login || location.pathname === "/login";
    const needsStateChange = onLoginRoute && !state.login;
    if (needsStateChange) {
      // we hit a URL with ?login in it
      // replace state with the same URL but login modal state
      props.history.setState({login: true});
    }
  }
  render() {
    return React.Children.only(this.props.children);
  }
}

如果您不想使用查询字符串来显示登录模式,那么您当然可以修改此组件以满足您的需要。

接下来是LocationDisplay,但它只是我为JSBin演示构建的一个组件,它显示了有关当前路径、状态和查询的信息,还显示了一组演示应用程序功能的链接。

登录状态对于下一个组件ModalCheck非常重要。该组件负责检查login(或profile,或可能的任何其他)键的当前状态,并酌情显示相关的模态。(JSBin演示实现了一个超级简单的模态,你的肯定会更好。:)它还在主页面上以文本形式显示了模态检查的状态。)

class ModalCheck extends React.Component {
  render() {
    const location = this.props.location;
    const state = location.state || {};
    const showingLoginModal = state.login === true;
    const showingProfileMoal = state.profile === true;
    const loginModal = showingLoginModal && <Modal location={location} stateKey="login"><LoginModal /></Modal>;
    const profileModal = showingProfileMoal && <Modal location={location} stateKey="profile"><ProfileModal /></Modal>;
    return (
      <div style={containerStyle}>
        <strong>Modal state:</strong>
        <ul>
          <li>Login modal: {showingLoginModal ? "Yes" : "No"}</li>
          <li>Profile modal: {showingProfileMoal ? "Yes" : "No"}</li>
        </ul>
        {loginModal}
        {profileModal}
        {this.props.children}
      </div>
    )
  }
}

其他一切都是相当标准的React路由器。唯一需要注意的是LocationDisplay中的Link,它显示了如何链接到应用程序中的各个位置,并在某些情况下显示模态。

首先,您当然可以使用查询字符串中的login键链接(和永久链接)到任何页面,要求它显示登录模式:

<Link to="/one" query={{login: 1}}>/one?login</Link>
<Link to="/two" query={{login: 1}}>/two?login</Link>

当然,您也可以直接链接到/login URL。

接下来,请注意,您可以显式地设置状态,以便显示模态,这将而不是更改URL。然而,它将保留在历史中,因此可以按预期使用back/forward,并且刷新将在同一背景页的顶部显示模态。

<Link to="/one" state={{login: true}}>/one with login state</Link>
<Link to="/two" state={{login: true}}>/two with login state</Link>

您还可以链接到当前页面,添加特定模态。

const path = props.location.pathname;
<Link to={path} state={{login: true}}>current path + login state</Link>
<Link to={path} state={{profile: true}}>current path + profile state</Link>

当然,根据您希望应用程序的工作方式,并非所有这些都适用或有用。例如,除非模态是真正的全局的(也就是说,无论路线如何,它都可以显示),否则这可能很好,但对于显示给定用户的配置文件等模态,我可能会将其作为一个单独的路线,并将其嵌套在父级中,例如:

<Route path="/users/:id" component={UserPage}>
  <Route path="/users/:id/profile" component={UserProfile} />
</Route>

在这种情况下,UserProfile将是一个呈现模态的组件。

另一个您可能想要进行更改的示例是在历史中存储某些模态;如果您不想,请根据需要使用replaceState而不是setState

使用shouldComponentUpdate做这件事非常简单

<Router history={newHistory}>
        <Route path="/" component={App}>
          <Route path="home" component={Container} >
            <IndexRedirect to="home"/>
            <Route path="login" component={Authentication}/>
            <Route path="home" component={Home}/>
            <Route path="post/:id" component={Post}/>
          </Route>
        </Route>
</Router>
<Link to='/post/123' state={{isModal:true}}/>
<Link to='/home' state={{isModal:false}}/>
<Link to='/login' state={{isModal:true}}/>
Container = React.createClass({
  render() {
    let isModal = (location.state && location.state.modal);
      return (
        <div id="MeContainer">
          <ModalControlOpen isModal={isModal}>
            <Modal returnTo={this.props.location.pathname}>
              {this.props.children}
            </Modal>
          </ModalControlOpen>
          <ModalControlClose isModal={isModal}>
            {this.props.children}
          </ModalControlClose>
        </div>
      )
   
  }
});
ModalControlOpen = React.createClass({
  render(){
    if (this.props.isModal) {
      return (
        this.props.children
      )
    } else return <div></div>
  }
});
ModalControlClose = React.createClass({
  shouldComponentUpdate(nextProp){
    return !nextProp.isModal
  },
  render(){
    return (
      this.props.children
    )
  }
});

最新更新