背景:
在使用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加進路由的嘗試:
// - 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>) ) }
// - 需要顯示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> ) }
最終使用的方法
引用:
title: Create a Modal Route with React Router
author: Jason Brown
url: https://codedaily.io/tutorials/77/Create-a-Modal-Route-with-React-Router
