最近在工作上有客製離頁提示的需求,最後透過React-Router的<Prompt /> component實現,在這邊紀錄跟分享一下踩到的坑以及最後的實作,希望能幫助到需要的人。
需要看原始碼的朋友可以參考我的Repo,以下為連結:
https://github.com/scps960740/customized-react-router-prompt-tutorial
需求是這樣的
在製作公司內部系統時,需要製作個提示匡避免使用者編輯過表單內容後,不小心跳轉到其他頁,這地方的確認提示框用 antd
實現。
最一開始製作的思路
- 用
useState
+useEffect
處理state,初始值為prop進來的when
。 - 當dialog點擊「確認」後透過
setIsShowPrompt(false)
修改state。 - 然後
history.push
換頁!
import React, { useState, useEffect } from 'react'
import modal from 'antd/lib/modal'
import { Prompt, useHistory } from 'react-router-dom'const CustomizedPrompt = ({ when }) => {
const history = useHistory()
const [ isShowPrompt, setIsShowPrompt ] = useState(when)
useEffect(() => {
setIsShowPrompt(when)
}, [when]) const messageHandler = (nextLocationObj) => {
modal.confirm({
title: '確定要離開?',
onOk: () => {
setIsShowPrompt(false)
history.push(nextLocationObj.pathname)
}
})
return false
}
return <Prompt message={messageHandler} when={isShowPrompt} />
}export default CustomizedPrompt
恩,看起來很美好,但這邊有個嚴重的Bug。
history.push(nextlocation.pathname)
執行的當下,setIsShowPrompt(false)
該觸發的render卻還沒執行,所以history.push執行的當下isShowPrompt
依然是true
,會再次被<Prompt />
擋下來,後來因為已經re-render了,所以第二次點擊可以跳正常出頁面。
- Bug執行的流程
- 點擊dialog「確認」後發生的事情
- history.push源碼,有個
ok
變數用來確定是否被<Prompt />
擋下
最後的思路
- 多設置一組
const [nextLocation, setNextLocation] = useState<any>(null);
。 - 按下「確認」後除了修改
isShowPrompt
,也修改nextLocation
。 - Re-render觸發的時候,確認
nextLocation
是否有值,有的話就跳轉頁面。
import React, { useState, useEffect } from 'react'
import modal from 'antd/lib/modal'
import { Prompt, useHistory } from 'react-router-dom'const CustomizedPrompt = ({ when }) => {
const history = useHistory()
const [ isShowPrompt, setIsShowPrompt ] = useState(when)
useEffect(() => {
setIsShowPrompt(when)
}, [when]) const [nextLocation, setNextLocation] = useState(null)
// 每次render確認有沒有next location state。
useEffect(() => {
if (nextLocation !== null) {
history.push(nextLocation)
}
}, [nextLocation]); const messageHandler = (nextLocationObj) => {
modal.confirm({
title: '確定要離開?',
onOk: () => {
setIsShowPrompt(false)
setNextLocation(nextLocationObj)
}
})
return false
}
return <Prompt message={messageHandler} when={isShowPrompt} />
}export default CustomizedPrompt
- 修改後的流程
這樣就完成需求囉!
需要看原始碼的朋友可以參考我的Repo,以下為連結:
https://github.com/scps960740/customized-react-router-prompt-tutorial
主要會踩到坑是因為自己對React render機制的不熟悉,以下附上我自己學習的文章來源,希望能幫助到有需要的人~謝謝大家。
Reference
- React hooks: not magic, just arrays
React hooks: not magic, just arrays
Untangling the rules around the proposal using diagrams
medium.com