我的应用程序中有公共区域和私有区域,我希望能够在公共视图中的任何位置显示登录模式对话框。模态应该有自己的路线。第二个用例是私有区域中的配置文件模式。
问题是当显示模态时背景中的当前视图会消失,因为模态不是当前视图路径的子视图。
由于我不想为每个可能的视图路径添加相同的模态,所以问题是:是否可以将模态路由与其父路由分离并在应用程序中的任何位置显示它们而不显示主内容?对此最好的方法是什么?我发现this issue但似乎不是同一个问题。
模态路线上的浏览器刷新在后台不会呈现任何内容,但这是我可以忍受的问题。
答案 0 :(得分:22)
我认为您最好的选择可能是使用隐藏的state
或查询字符串(用于永久链接),或两者兼而有之,尤其是如果可以在任何页面上显示模态(例如登录模式)。如果您不了解,React路由器会公开历史记录API的state
部分,允许您将数据存储在用户的历史记录中,而这些记录在网址中实际上不可见
这是我想到的一系列路线;如果你愿意,你可以跳straight into the working example on JSBin。您还可以查看生成的示例应用in its own window,以便您可以看到更改的网址(它使用哈希位置策略与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
是一个有一个目的的组件:如果有{{1},请检查路径是/login
还是查询当前路径上的字符串。如果其中任何一个为真,和当前状态不包含?login
密钥,则会将状态上的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);
}
}
,但它只是我为JSBin演示构建的一个组件,它显示有关当前路径,状态和查询的信息,还显示了一组演示应用程序和#39;功能。
登录状态对于下一个组件 LocationDisplay
非常重要。该组件负责检查ModalCheck
(或login
或其他任何其他)键的当前状态,并根据需要显示关联的模式。 (JSBin演示实现了一个超级简单的模态,你的肯定会更好。:)它还会在主页面上以文本形式显示模态检查的状态。)
profile
其他一切都是相当标准的React Router的东西。唯一需要注意的是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>
)
}
}
里面Link
s,它显示了如何链接到应用中的各个地方,在某些情况下显示模态。
首先,您当然可以链接(和永久链接)任何页面,要求它使用查询字符串中的LocationDisplay
键来显示登录模式:
login
您当然也可以直接链接到<Link to="/one" query={{login: 1}}>/one?login</Link>
<Link to="/two" query={{login: 1}}>/two?login</Link>
网址。
接下来,请注意您可以显式设置状态以便显示模式,这将不更改URL。但是,它会在历史中持续存在,因此可以按照您的预期使用后退/前进,并且刷新将在相同的背景页面上显示模态。
/login
您还可以链接到当前页面,添加特定模式。
<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
。
答案 1 :(得分:0)
使用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
)
}
});