react-route v3
項目中寫了一個自動生產文檔的系統, 其中有一個比較煩人的需求,就是項目中的面包屑是根據
Router組件自動生成的,它里面是根基Router組件的配置層級自動生產, 比如A -> B -> C 那么
Router先匹配A 然后 B 然后C, 目前有個需求是,C下面有兩個子組件,他們是switch關系,即只能是其中一個, 這兩個子組件一個是文檔的列表,一個是文檔的詳情,項目要求在文檔的詳情頁面通過點擊面包屑應該返回到文檔列表頁碼,
怎么辦?
初步想法是 使用react-router的 onEnter onLeave 等生命鉤子
查看文檔 onEner onLeave
react-router route組件
onLeave 函數的 route 組件的函數 , 注意是 route組件的函數, 并不是route組件對應的component組件的函數(即我們自己寫的組件), 相當于在外面嵌套了一層,所以你直接在使用組件寫的route組件中使用這個函數是沒有用的
class R extends React.Component {
onLeave(){} // 無效的 onLeave是route組件的鉤子函數
}
<Router>
<Route
componnet={R}
onLeave={} //應該是在這里寫
>
</Route>
</Router>
所以首先明白鉤子是輸入誰的。
問題又來了
onLeave 里面并沒有 replace, 即我不能執行切換路由,而且最重要的是 onLeave里面只有 preState 即
我只能自到我目前在那,并不能知道我將其去哪?咋辦
所以,這個函數已經無法使用了
我們可以想到一點是, 既然我們想要使用replace這樣的函數,而且又能知道我們處于哪個位置,那么是
不是直接在組件生命周期里面使用會不會更加方便一點?
于是
class R1 extends React.Component {
}
class R2 extends React.Component {
componentWillUnmount() {
this.props.router.push('xxxx'); // 當R組件離開的時候 切換路由
}
}
<Router>
<Route
componnet={C}
onEnter={ redirect to R1} // 進入一C的時候 直接導入R1
>
<Route component={R1} />
<Route component={R2} />
</Route>
</Router>
問題來了,面包屑上一級是 C ,返回C 就會直接導入R1, 而我們想要的是 從R2 已經導入到其他我們想要
的路由,就我們上面想的,在R2 的 componentWillUnmount 這里監聽,當R2變化的時候,直接導向其他的路由,是不少符合我們的需求了,還是太天真了
并不行,因為 onEnter 先發生 然后 R2的componentWIllLeave 才發生, 其實是這樣的,onEnter其實在
history這里監聽, 當點擊 link的時候 先觸發 history的 location 鏈接修改,然后這個鏈接一旦修改,馬上觸發 onEnter發送,后面才是組件發送改變。所以這個方法也被否定了。
有沒有既在組件里面寫的路由,又在history location 鏈接改變的時候觸發的
當然有 routerWillLeave 這是路由組件將要離開的時候觸發的, 而且可以寫在我們定義的組件里面
具體用法為:
componentDidMount(){
this.props.router.setRouteLeaveHook(
this.props.route,
this.routerWillLeave
)
routerWillLeave(nextLocation){
return 'Are you sure you want to leave?';
}
用法討論
上面那個方法恰好符合我的要求, 而且, 它實在history層面監聽的,所以發送在C的onEnter之前。
于是我在里面使用為
componentDidMount(){
this.props.router.setRouteLeaveHook(
this.props.route,
this.routerWillLeave
)
}
routerWillLeave = (nextLocation) => {
this.props.router.replace("....path");
}
結果還是有問題了,無限循環, 怪不得 onLeave不提供 replace 這些函數,原來如此,因為routeWllLeave 是不斷的調用的,因為當前組件還沒離開,然后有 replace 然后又執行routeWillLeave
當然, 解決這個很簡單, 配加個判斷就行了
componentDidMount(){
this.props.router.setRouteLeaveHook(
this.props.route,
this.routerWillLeave
)
}
routerWillLeave = (nextLocation) => {
if (nextLocation.pathname === "targetPath) {
this.props.router.replace("....path");
}
}
解決了!