博客 / 詳情

返回

路由跳轉Bug:React+dva數據流頁面跳轉,路由發生變化但頁面不刷新問題及解決方案(相同路徑,不同參數)

React+Dva.js項目中路由跳轉成功(瀏覽器url發生變化)但頁面不重新加載

1.問題描述:

在最近項目中,遇到了這個問題,如下圖,項目中採用了和瀏覽器類似的多標籤頁面顯示。
image.png
當我點擊紅色框中的按鈕,希望實現頁面跳轉,並將被點擊數據的序號作為參數,跳轉傳入下一個頁面。
image.png

2.Bug描述:

當我在標籤頁中點擊序號為71的數據進行路由跳轉,再將序號為71的數據跳轉到頁面關閉後,重新點擊其他數據,可以正常渲染。但是當我頁面沒有關閉,重新點擊除序號71以外的數據,路由發生跳轉,但頁面仍然停留在序號為71數據跳轉後的頁面
image.png

3.Bug原因:

通過分析發現當點擊第一個數據執行路由跳轉後的頁面未關閉,點擊第二個數據執行路由跳轉第二次傳入的參數Id,雖然路由改變,但此時並未識別為一個新的頁面,props中數據只有history的路由相關信息發生改變,此時並未重新執行React的生命週期,因此compontentDidMount中的dispath請求並未發出,因此數據未更新,所有的頁面渲染都是建立在前一個頁面數據的基礎上。

4.解決方案

由於props數據中發生改變,因此我們可以使用React生命週期中的static getDerivedStateFromProps()方法,具體的使用可以看我的另一篇文章:“React新生命週期getDerivedStateFromProps的理解與使用”,不要使用componentWillReciveProps()方法,這個方法在React新的證明週期中已經被取消。
1.在getDerivedStateFromProps()方法中使用window.location.reload();
注意:使用此方法,需要在對props中的路由參數及preState中的路由參數做判斷,如果不相同才可以window.location.reload(),否則,會導致dispatch請求陷入死循環。
優點:重新執行React完整生命週期
缺點:只重新加載當前標籤頁,其餘標籤頁自動關閉,同時用户體驗感極差,與系統整體加載頁面違和。
2.在getDerivedStateFromProps()方法中,重新調用compontentDidmount中的dispatch請求。
注意:同樣要注意,對props中的路由參數及preState中的路由參數做判斷
優點:不會和方法1一樣造成其他標籤頁關閉,不會與系統整體加載頁面違和。如果dispatch方法有callback需要的情況下,使用這種方法會省去很多麻煩。
缺點:不會正常執行React生命週期的constructor()方法,所有的頁面的渲染,都是用新的數據去替代上一個頁面的數據,包括state中的數據。
3.在Models中添加subScriptions即訂閲。
這個方法也是Dva.js的官方解決辦法,解決原理與方法2沒有太大差異,都是重新發起請求,然後用數據的變化驅動頁面的渲染。為Models添加subScriptions屬性,與namespace,state,*effect等同級。
image.png
具體實現代碼如下:

  subscriptions: {
    setupHistory({dispatch, history}) {
      history.listen((location) => {
        const {pathname} = location;
        //在此取到新的路由
        const reg = /(?<path>\/testCase\/Execute\/)(?<currentTestCaseId>\d+)/g;
        const regCopy = /(?<path>\/testCase\/Execute\/)(?<currentTestCaseId>\d+)/g;
        if (reg.test(pathname)) {
        //對路由進行正則匹配
          const {groups: {currentTestCaseId}} = regCopy.exec(pathname);
        //重新發送請求
          dispatch({
            type: 'cleanDetail',//注意,此處不需要加上該Model的NameSpace因為在Model中使用
            payloade:{
            currentTestCaseId,
          });
        }
      })
    }
  },

注意:subScriptions一旦在頁面的Models中設置,當訪問到該頁面後,其餘的所有頁面都會自動調用此方法如果不對路由路徑進行判斷時,就會出現在用其他頁面的其他參數,發送該頁面請求的問題。同時,如果很多頁面需要用到subScriptions,建議放到初始佈局Model中,以Antd佈局為例,建議放到basicdata.js的Model文件中,只需要對需要subScriptions操作的頁面路徑進行判斷情況的增加即可,原因:剛剛説到,只要執行到這個Model文件,便創建了subScriptions,後續一直執行監聽,因此我們只需要對需要的執行該操作的路徑進行if和正則判斷,這樣不僅便於管理和維護,同時也省去了很多代碼。
注意:在basicdata.js中發送dispatch請求的時候,type傳入的路徑就需要帶上對應Model的namespace。
image.png

subScriptions簡單介紹以及常見的幾種監聽:

model中的subscription相當於⼀個監聽器,可以監聽路由變化,⿏標,鍵盤變化,服務器連接變化,狀態變化等,這樣在其中就可以根據不同的變化做出相應的處理,在這個subsription中的⽅法名是隨意定的,每次變化都會⼀次去調⽤⾥⾯的所有⽅法,所以⼀邊會加相應的判斷。

subscriptions: {
    setup({ dispatch, history }) {  // ⽅法名setup可以隨便命名
      window.onresize = () => {   //這⾥表⽰的當瀏覽器的頁⾯的⼤⼩變化時就會觸發⾥⾯的dispatch⽅法,這⾥的save就是reducers中的⽅法名
        dispatch (type:"save")  
      }
    },
    onClick ({dispatch}) {
      document.addEventListener('click',() => {   //這⾥表⽰當⿏標點擊時就會觸發⾥⾯的dispatch命令,這⾥的save就是reducers中的⽅法名
        dispatch (type:"save")
      })
    }
  },
  setupHistory({dispatch,history}){
    history.listen((location) => {
      console.log(location)   //這⾥可以獲取當前變化的history路徑以及參數,hash所有值,這樣就可以在路由地址變化後做處理
      ....
    })
  }
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.