背景:
在使用react-router时,我们往往只有在真实的需要页面跳转时,才会将页面加入路由(Route)。对于弹框(Modal),却总是会忽视它的路由需求。
在开发者眼中: 弹框只是页面中的附属,无需单独准备路由存放。当然,也有其他原因,比如我们想要一个公用的方法,以此来显示弹框。诸如此类,导致开发时没有正视弹框。
可在用户眼中: 弹框也是页面,至少在想要返回的时候,移除弹框即可(不要把弹框下的页面也给返回了)。
因此,Modal弹框、Route路由、BackPress物理返回 三者间的微妙关系就变得尤为关键了
目的:
用户视角: 弹框显示时,物理返回(手机返回操作、浏览器左上角回退按钮),只隐藏弹框(不影响下层页面内容,比如滚动、比如输入框内容)
开发者视角: 1. 原本的目的很简单,只需要在有弹框显示时,物理返回隐藏弹框的同时阻止下层页面的返回
2. 将弹框加入路由,弹框即页面,会被路由同等对待
几次尝试:
1. 使用 popstate 监听
window.addEventListener("popstate", () => {});
2. 使用history.listen监听
history.listen((newLocation, action) => { if(action === "PUSH) { } else { // 判断是否存在弹框 if(ReactDOM.findDOMNode(document.getElementById("modal"))) { // 隐藏弹框
// 前进一个路由 history.go(1) } } })
注意: 以上方法均是通过监听路由变化(popstate监听、history.listen监听),并辅以判断弹框的显示与否来确定。
此处使用的是history.go(1)前进一个路由,因为无法阻止在物理返回时的路由回退,只能通过 回退 - 判断有弹框 - 前进 的流程进行
将Modal加进路由的尝试:
方式一: 使用 <Route path="/" /> + this.props.location.search改变路由
- 特点: 通过跳转路由,在原有url上增加"?login=true",PUSH路由,以此判断识别是否显示指定内容
// - Router.js中使用: <BrowserRouter> <Switch> ... </Switch> <Route path="/" component={Login}> </BrowserRouter> // - 显示Modal(跳转Login路由): <Linl to={{pathname: this.props.match.url, search: "?login=true"}}>To Login</Link> // - Login.js中使用 render() { let params = new URLSearchParams(this.props.location.search); return( params.get("login") && (<Modal>...</Modal>) ) }
方式二: 在需要页面增加<Route>节点 + path={this.props.match.url + "/modal"} + React.createPortal(<div>...</div>, #modal_root) + index.html中的 <div id="modal_root"></div>
- 特点: 在需要的页面增加<Route>节点,且Route的path为当前url(即this.props.match.url) + "/modal",另外Route的渲染内容(render)和通过React.createPortal和外部index.html的<div id="modal_root"></div>关联
// - 需要显示Modal的 profile.js <Link to={this.props.match.url + "/modal"}>Show Modal</Link> <Route path={this.props.match.url + "/modal"} render={() => { return ( <Modal onClick={() => this.props.history.goBack()}> ... </Modal> ) }} /> // - Modal.js 内容: render() { return React.createPortal( <div onClick={this.props.onClick}> ... </div> ) }
最终使用的方法
1. 使用路由跳转(本处使用的search),但是仅仅跳转,路由中没有实际内容;
2. 原本的Modal显示不变方式;
3. 在Modal的show()方法中,增加history.push({pathname: this.props.match.url, search: "?modal=true"});
4. 增加 history.listen监听,判断带有相应search的路由回退,即可关闭想要Modal;
5. 在Modal的hide()方法中,增加history.go(-1),在Modal隐藏时,回退路由。
引用:
title: Create a Modal Route with React Router
author: Jason Brown
url: https://codedaily.io/tutorials/77/Create-a-Modal-Route-with-React-Router