react-router4提供了prompt组件,用于在路由改变之前触发确认框,点击确认可以进行跳转,点取消可以取消跳转保持在当前路由内。它底层的实现应用了原生JS的confirm,弹出的效果也跟confirm一样,但在日常的开发中,这种confirm效果肯定不是我们想要的,但也无法通过CSS来修改,不过仔细阅读一下react-router4的官方文档,可以知道在根路由上有getUserConfirmation事件,在这个事件中就可以控制prompt。
在应用react-router4定义路由的时候,在根路由组件上定义一下getUserConfirmation方法,这个方法有两个参数,分别是message、callback,其中message是传递给prompt组件的message的值,而当执行callback(true)时,相当于点击确认按钮,执行callback(false)时,相当于点击取消按钮。
1 | <BrowserRouter getUserConfirmation={getConfirmation}> |
在定义getConfirmation函数的时候,要注意此函数返回一个自定义的组件,而这个组件不能加载到root的根div上,需要在index.html中再定义一个div,将自定义的组件挂载到这个div上,否则就会替换掉root上的所有内容。其主要原理是,只有在我们需要的时候,才会显示这个组件,不需要的时候,不要显示。
1 | getConfirmation = (message, callback) => { |
这里为了方便应用了antd的Modal组件,其实也可以自定义样式。以上的方法定义完之后,只要再需要的地方应用一下prompt组件,就可以直线跳转之前拦截。简单写一下代码:
1 | class ComponentA extends Component { |
通过props和state控制一下urlChangeShowModal的真假,就可以实现路由改变之前的确认提示。其实以上的内容在简书里也有类似的文章,但是在实际的开发中,会应用到状态管理,像redux、mobx等工具,那如何在应用这些工具的时候,再进行控制呢?
正常在开发中我们会在入口文件index.js中,采用react-redux或者mobx-react中的Provider来使得在任意组件中都能拿到想要的数据,其实在我们刚才自定义的确认框组件中也是一样应用,不过有个注意点,确认框中用的store不能直接引入项目中的store,而是要使用与入口文件中一样的store,否则状态就会不统一,那边改变了,这边还时初始化的数据。这里我使用的是mobx,redux也是一样的道理
1 | 入口文件index.js |
更改一下上面写的自定义的Prompt组件
1 | import {stores} from '../index.js' |
这样在ComponentA组件中,如果用mobx的changeUrlChangeShowModal方法来控制urlChangeShowModal的true或false,同样可以实现路由跳转确认提示。
以上就是自定义Prompt组件,以及在redux或者mobx中应用的所有内容。
在实际的开发中,还遇到了一个问题,就是应用了antd组件实现左侧菜单的时候,菜单传递了组件的url,但是当Prompt中的when为true的时候,点击当前页面的菜单,还是会弹出确认提示框,但此时路由的url和菜单的url是一样的,并未发生跳转,为什么还会执行Prompt提示呢?研究了好久,感觉应该是菜单上定义的url会替换当前的url,只是替换前后是一样的。
但这种效果并不是想要的,因为在当前的页面上,点击当当前页面的菜单,还会提示,不合理,所以需要进一步更改,但试了很多办法,都不能达到想要的效果,最后应用了一个暴力的解决方案:(还是应用上面的例子)
1 | 1、用window.location.href获取当前的url |
这样可以实现想要的效果,不过感觉这种方案并不完美,但目前还没想要其它办法,如果尝试到更好的办法会进一步补充。