React單頁應(yīng)用自定義頁面離開確認(rèn)彈框的方法

問題描述

項(xiàng)目技術(shù)棧是react+ant-design趁桃;

產(chǎn)品需求是在某個頁面表單有信息輸入或變更雁歌,用戶未保存要離開該頁面時呻引,自定義提示用戶提示颜及,如果用戶確認(rèn)離開則跳轉(zhuǎn)到新頁面掖肋,否則留在當(dāng)前頁仆葡。

需求UI-已實(shí)現(xiàn)的.jpg

思考

在react單頁應(yīng)用中,需要根據(jù)用戶的選擇來是否跳轉(zhuǎn)頁面志笼,也就是需要阻塞瀏覽器的頁面跳轉(zhuǎn)沿盅,類似<a>標(biāo)簽點(diǎn)擊后的return false效果,待到用戶操作后再執(zhí)行后續(xù)操作纫溃。

首先腰涧,能想到的是瀏覽器的window.confirm方法,在交互上完全符合需求紊浩,那么自定義需求呢窖铡?經(jīng)過查閱資料發(fā)現(xiàn),目前主流瀏覽器都不支持該方法樣式的自定義坊谁,因此放棄费彼。

其次,就是react的方案口芍,react中箍铲,控制頁面路由的是react-router,若官方提供了這種能力便是更好的鬓椭。于是颠猴,我查閱了其官方文檔:http://react-guide.github.io/react-router-cn/docs/API.html关划、或者https://reacttraining.com/react-router/core/api/Prompt、中文版https://segmentfault.com/a/1190000014294604?utm_source=tag-newest

關(guān)鍵文檔

由于項(xiàng)目中使用的是react-router-v4翘瓮,所以直接查看最新的API(如果是較老版本有比較簡單的實(shí)現(xiàn)贮折,可參考https://blog.csdn.net/ISaiSai/article/details/53908903

一、Prompt組件

import { Prompt } from 'react-router-dom';
....
render(){
  return(
      <Prompt message="確定要離開春畔?"  when={true}/>
  )
}

如上代碼脱货,可以使用react-router-dom的Prompt,在JSX的任意render方法中(不僅僅是在router組件中)都可以使用律姨,使用后當(dāng)前頁面組件離開就會給用戶提示信息振峻,默認(rèn)彈框?yàn)闉g覽器的window.confirm彈框。

  1. message: string
    當(dāng)用戶離開當(dāng)前頁面時择份,設(shè)置的提示信息扣孟。
    <Prompt message="確定要離開?" />
  2. message: func
    當(dāng)用戶離開當(dāng)前頁面時荣赶,設(shè)置的回掉函數(shù)凤价,返回 true 時允許直接跳轉(zhuǎn)
    <Prompt message={location => (
    Are you sue you want to go to ${location.pathname}?
    )} />
  3. when: bool
    通過設(shè)置一定條件要決定是否啟用 Prompt,屬性值為true時啟用防止轉(zhuǎn)換拔创;

二利诺、Router組件的getUserConfirmation方法

用于確認(rèn)導(dǎo)航的函數(shù),默認(rèn)使用 window.confirm剩燥。例如慢逾,當(dāng)從 /a 導(dǎo)航至 /b 時,會使用默認(rèn)的 confirm 函數(shù)彈出一個提示灭红,用戶點(diǎn)擊確定后才進(jìn)行導(dǎo)航侣滩,否則不做任何處理。需要配合 <Prompt> 一起使用变擒。

const getConfirmation = (message, callback) => {
  const allowTransition = window.confirm(message);
  callback(allowTransition);
}

<BrowserRouter getUserConfirmation={getConfirmation} />

實(shí)現(xiàn)方案

中心思想:阻塞頁面君珠,等待用戶操作后異步處理頁面跳轉(zhuǎn)(router變更)。

探索一:Prompt既然是組件娇斑,那么在組件內(nèi)使用state變更的方式去render是否可行策添?

嘗試如下代碼:

...
<Prompt
   message = {(location)=>{
     return this.state.customPromt,
     }
   }
  />
...

實(shí)踐證明這是不行的,prompt組件的message方法被同步調(diào)用了毫缆,不能達(dá)到阻塞頁面跳轉(zhuǎn)的效果唯竹。

探索二:嘗試async/await

既然是要異步執(zhí)行,那么就可以嘗試async/await的異步處理方式悔醋,同步寫摩窃。恰好message也可以是一個function。于是,新的嘗試代碼如下:

...
handlePrompt = async () => {
    const leaveConfirm = new Promise((res, rej) => {
        confirm({
            content: 'Are you sure?',
            onOk() {
                res(true);
            },
            onCancel() {
                res(false);
            },
        });
    });

    const leave = await leaveConfirm;
    return leave ;
}
...
<Prompt message={(location) => {this.handlePrompt} />
...

滿懷期待猾愿,然后...還是不行鹦聪。仔細(xì)查閱文檔發(fā)現(xiàn),message方法返回的是一個字符串或者true蒂秘,返回字符串則調(diào)用瀏覽器默認(rèn)的prompt方法阻塞頁面跳轉(zhuǎn)泽本,和直接使用window.prompt同效果,不符合需求姻僧;返回true則直接跳轉(zhuǎn)规丽。

message: func

Will be called with the next location and action the user is attempting to navigate to. Return a string to show a prompt to the user or true to allow the transition.

以上嘗試代碼,雖然添加了異步方法撇贺,也會執(zhí)行異步函數(shù)赌莺,但是始終會跳轉(zhuǎn)頁面,經(jīng)查原因?yàn)椋?a target="_blank">https://github.com/ReactTraining/react-router/issues/6669松嘶。

探索三:getUserConfirmation方法艘狭,https://github.com/ReactTraining/history#customizing-the-confirm-dialog

前兩種方案都失敗了,那么再次閱讀文檔發(fā)現(xiàn)翠订,getUserConfirmation方法就是提供給我們自定義用的巢音,默認(rèn)情況下,當(dāng)頁面使用了prompt組件后尽超,調(diào)用的getUserConfirmation方法是瀏覽器默認(rèn)的window.prompt官撼。如果需要自定義,直接覆蓋即可似谁。

簡單示例代碼如下:

const getConfirmation = (message, callback) => {
  const allowTransition = window.confirm(message);
  callback(allowTransition);
}

<BrowserRouter getUserConfirmation={getConfirmation} />

ps:注意該方法需要寫在BrowserRouter 或 MemoryRouter 上傲绣。

那么接下來的問題,就是將自定義的或者其他UI組件融合到該方法內(nèi)棘脐。
已實(shí)現(xiàn)的代碼如下:

import { Modal } from 'antd';
...
function getConfirmation(message, callback) { // 至關(guān)重要的callback方法斜筐,可以異步執(zhí)行
    if (!G.pageChangeConfirm) { // G.pageChangeConfirm為頁面內(nèi)的全局變量龙致,用于數(shù)據(jù)交互與條件判斷
        callback(true);
        return;
    }
    Modal.confirm({
        title: '離開該頁面蛀缝,表單信息將不被保留?是否確定離開該頁面目代?',
        content: '',
        okText: '離開',
        cancelText: '取消',
        onOk() {
            callback(true);
        },
        onCancel() {
            callback(false);
        },
    });
}

ReactDOM.render((
    <BrowserRouter
        getUserConfirmation={getConfirmation}
    >
        <App />
    </BrowserRouter>
    , document.getElementById('react-wraper')
);
...

探索四:查看GitHub的issue屈梁,查找其他解決方案

花了半天時間,在issue中找到了這個帖子https://github.com/ReactTraining/react-router/issues/4635榛了,感興趣的可以看下討論過程在讶。其中提到了兩種解決方案:

  1. getUserConfirmation,類似我上方的解決方案霜大,可運(yùn)行的完整參考代碼:https://codepen.io/pshrmn/pen/MpOpEY

  2. history.block构哺,利用history的API,需要withRouter包裝,傳送門:https://github.com/ReactTraining/react-router/issues/4635#issuecomment-297828995

探索五:閱讀源碼曙强,https://unpkg.com/react-router-dom@5.0.1/umd/react-router-dom.js残拐,該部分工作沒有做完,感興趣的可以先研究研究碟嘴,這里先把代碼拿出來溪食。

結(jié)論:有至少2種方法可以實(shí)現(xiàn)prompt自定義,getUserConfirmation和history.block娜扇,如果您有更好的解決方案错沃,期待您的反饋。

最后:以上方法頁面刷新時無效雀瓢,那么刷新頁面怎么實(shí)現(xiàn)類似功能呢枢析?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市刃麸,隨后出現(xiàn)的幾起案子登疗,更是在濱河造成了極大的恐慌,老刑警劉巖嫌蚤,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辐益,死亡現(xiàn)場離奇詭異,居然都是意外死亡脱吱,警方通過查閱死者的電腦和手機(jī)智政,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來箱蝠,“玉大人续捂,你說我怎么就攤上這事』掳幔” “怎么了牙瓢?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長间校。 經(jīng)常有香客問我矾克,道長,這世上最難降的妖魔是什么憔足? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任胁附,我火速辦了婚禮,結(jié)果婚禮上滓彰,老公的妹妹穿的比我還像新娘控妻。我一直安慰自己,他們只是感情好揭绑,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布弓候。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪菇存。 梳的紋絲不亂的頭發(fā)上彰居,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天,我揣著相機(jī)與錄音撰筷,去河邊找鬼陈惰。 笑死,一個胖子當(dāng)著我的面吹牛毕籽,可吹牛的內(nèi)容都是我干的抬闯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼关筒,長吁一口氣:“原來是場噩夢啊……” “哼溶握!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蒸播,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤睡榆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后袍榆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胀屿,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年包雀,在試婚紗的時候發(fā)現(xiàn)自己被綠了宿崭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡才写,死狀恐怖葡兑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情赞草,我是刑警寧澤讹堤,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站厨疙,受9級特大地震影響洲守,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜轰异,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一岖沛、第九天 我趴在偏房一處隱蔽的房頂上張望暑始。 院中可真熱鬧搭独,春花似錦、人聲如沸廊镜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至配椭,卻和暖如春虫溜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背股缸。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工衡楞, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人敦姻。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓瘾境,卻偏偏與公主長得像,于是被迫代替她去往敵國和親镰惦。 傳聞我的和親對象是個殘疾皇子迷守,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容