在最近项目中,遇到了这个问题,如下图,项目中采用了和浏览器类似的多标签页面显示。
当我点击红色框中的按钮,希望实现页面跳转,并将被点击数据的序号作为参数,跳转传入下一个页面。
当我在标签页中点击序号为71的数据进行路由跳转,再将序号为71的数据跳转到页面关闭后,重新点击其他数据,可以正常渲染。但是当我页面没有关闭,重新点击除序号71以外的数据,路由发生跳转,但页面仍然停留在序号为71数据跳转后的页面。
通过分析发现当点击第一个数据执行路由跳转后的页面未关闭,点击第二个数据执行路由跳转第二次传入的参数Id,虽然路由改变,但此时并未识别为一个新的页面,props中数据只有history的路由相关信息发生改变,此时并未重新执行React的生命周期,因此compontentDidMount中的dispath请求并未发出,因此数据未更新,所有的页面渲染都是建立在前一个页面数据的基础上。
由于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等同级。
具体实现代码如下:
subscriptions: {
setupHistory({dispatch, history}) {
history.listen((location) => {
const {pathname} = location;
//在此取到新的路由
const reg = /(?
const regCopy = /(?
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。
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所有值,这样就可以在路由地址变化后做处理
....
})
}