IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> MemoryRouter离开页面弹窗确认 -> 正文阅读

[JavaScript知识库]MemoryRouter离开页面弹窗确认

版本:都是最新版本
React17 + React-Router5
使用create-react-app搭建的工程

0.前言

可能许多人和我的场景一样,第2点讲述了我的问题发生背景,可以跳过,但这也是我选择MemoryRouter的原因。使用MemoryRouter只是一种方式,可以看下第4点的其他方式。

1.需求描述

离开表单页前,如果表单还没有保存,需要让跳转路由的操作停下来,弹出确认弹窗让用户判断是否的确是不保存离开页面。

2.问题发生背景

本来是用HashRouter做的SPA应用,就是#/xxx这种页面路由。

没有尝试BrowserRouter是因为需要后端配合。

然后页面组件内使用Prompt组件。

或者搭配使用HashRouter的getUserConfirmation的属性,这里不再赘述该方法的使用,与Prompt的message大体一致。并且我觉得还不太好用,因为太顶层了(在App.js这种顶层入口中,设置HashRouter组件的地方),很多状态(history、redux的state等)都拿不到,拿到了还可能会有莫名的错误交互(尝试使用useSelector获取redux的state,结果导致页面资源加载两次)。

1)Prompt

看一下官网文档https://reactrouter.com/core/api/Prompt
在这里插入图片描述
简单翻译下,Prompt组件的用处就是在离开页面前向用户给出确认提示。该组件有2个属性message和when。

when是个布尔值,为true时说明需要拦截路由,为false时则说明用户可以直接离开页面不需要确认提示。when非必传。

message可以是字符串或函数。当为字符串时,使用浏览器的window.confirm弹出确认提示,提示语为message。当为函数时,抛出2个数据(location, action),在方法中需要返回一个布尔值,当不需要弹出提示时返回布尔值true,当需要弹出提示时返回布尔值false。抛出参数的location是即将去向的路由数据;action是跳转方式,比如通过history.push跳转的话action就是字符串PUSH,如果是通过history.replace跳转的话action就是REPLACE。

2)Prompt的应用

网上查到的相关文章基本都是“如何自定义弹窗”。就是用when和message搭配使用,when用来判断页面是否需要弹窗,message中使用比如ant design的Modal.confirm。当onOk时,用拿到的location做页面跳转(可以在这里维护面包屑),onCancel的时候什么都不用写,主要原因是不容易拿到当前在哪个路由了,因为这个时候路由已经变了

问题就出在“因为这个时候路由已经变了”,再举例描述一下这里的操作:使用history.push({pathname:'home'})离开页面时,弹出了确认弹窗 的同时 页面url的路由已经变成了#/home,而且就算点“取消”,不离开页面,路由也变不回来了。这个情况就很糟糕了:路由变了,而页面还在表单页;再点同一个按钮离开页面,就不会执行跳转了,因为路由已经变了,结果页面也不弹窗、也不跳转。

3)可以看下大佬说的

issue: https://github.com/remix-run/history/issues/690

直接看谷歌翻译的吧:
在这里插入图片描述

也就是因为浏览器本身以及history.block的限制,导致虽然路由变了,而页面还在表单页。

3.使用MemoryRouter解决问题

MemoryRouter就是使用内存存储history,一般用在移动端、客户端等不使用url直接访问指定页面的情况。因为页面不使用路由区分,也就是url对应到index.html以后,无论#/xxx后面写什么都是跳转到同一个设置为#/的页面。

不知道是不是我百度关键词有问题,都没有看到什么MemoryRouter的实践。

App.js中代替HashRouter:

import React from 'react';
import { MemoryRouter as Router, Switch, Route } from 'react-router-dom';
import routes from 'router/index';

function App() {
    return (
        <Router>
            <Switch>
                {routes.map(route => (
                    <Route key={route.path} component={route.component} exact
                        path={`/${route.path}`}
                    />
                ))}
            </Switch>
        </Router>
    );
}
export default App;

我是将离开页面二次确认这部分逻辑提出到了高阶组件中,这样后面的页面中最后包一层即可。

看代码前,看一下使用MemoryRouter后的history数据结构:
在这里插入图片描述
页面历史就存储在history的entries数组里面,当前路由就是entries的最后一项。而history的一些跳转方法push、replace、go等都还在,也就是业务代码中可以像正常使用HashRouter一样,使用history.push等去做页面跳转,使用上没有差异,只是url不会有变化。

pageWrap.js高阶组件:

import React, { useState } from 'react';
import { Prompt, useHistory, useLocation } from 'react-router';
import { Modal } from 'antd';

// 页面离开前的路由拦截
function LeaveHOC(WrappedComponent) {
    return function HOC(props) {
    		 //这里需要用到history,顺便获取location,代替withRouter将history和location传入props
        const history = useHistory();
        const location = useLocation();
        //设置弹出弹窗时机
        const [when, setwhen] = useState(false);
        return (
            <>
                <Prompt
                    when={when}
                    message={(location, action) => {
                        Modal.confirm({
                            title: "确认离开吗?",
                            onCancel: () => { },//不离开页面
                            onOk: () => {//确认离开
                                setwhen(false);
                                setTimeout(() => history.replace(location), 0);//使setwhen生效后做跳转,避免when仍为true跳转后又弹出确认弹窗
                            }
                        });
                        return false;
                    }}
                />
                <WrappedComponent
                    {...props}
                    setwhen={setwhen}
                    history={history}
                    location={location}
                />
            </>
        );
    };
}

export default LeaveHOC;

在表单页中,如果发现表单修改了就要设置when为true使离开拦截路由二次确认,如果表单保存了就设置when为false正常离开页面。

表单页示例:

import React from 'react';
import { Form, Button, message } from 'antd';
import FormItems from 'common/components/formItems';//表单项组件(不用管)
import service from 'src/service';//请求方法(不用管)
import pageWrap from 'router/pageWrap';//高阶组件(上述主要逻辑)
import formConfig from './config';//表单项配置(不用管)

function ExampleForm({ history, setwhen }) {
    // 表单数据修改
    const onValuesChange = (e) => {
        setwhen(true);// 操作过表单,离开页面需要二次确认
    };
    // 表单提交
    const onFinish = (formData) => {
        // 触发提交请求
        service.submitForm(formData).then(() => {
            message.success('操作成功!');
            // 保存后,离开页面不需要二次确认
            setwhen(false);
            // when改值生效后跳转页面,避免立马跳转导致when仍为true,又弹出窗的情况
            setTimeout(() => history.goBack(), 0);
        });
    };
    return (
        <Form
            name="basic"
            labelCol={{ span: 3 }}
            wrapperCol={{ span: 20 }}
            onFinish={onFinish}
            onValuesChange={onValuesChange}
            initialValues={{}}
        >
            <FormItems options={formConfig} />
            <Form.Item wrapperCol={{ offset: 11, span: 2 }} key="submit">
                <Button type="primary" htmlType="submit">
                    保存
                </Button>
            </Form.Item>
        </Form>
    );
}
// 导出前包一层高阶组件
export default pageWrap(ExampleForm);

在自己的应用中,离开前路由拦截的问题是能解决了。虽然有个弊端是不能通过url访问指定页面,但我认为这个弊端是在可接受范围内的,因为其实真正的用户不会关心url到底是什么的,也不会去背下url(可以收藏url),大部分都是在系统里点点点进去的,就像用户在移动端、客户端根本不需要知道url,也根本没有输url的地方。所以如果应用在需求上这一点是无关紧要的话,是可以考虑下我这种方式的。

写到这里还是有问题没有处理的,就是浏览器的关闭按钮、回退按钮、刷新等,后续再考虑。

4.当然有别的思路啦

还是第2点中的那个issue链接: https://github.com/remix-run/history/issues/690

往下看:
在这里插入图片描述感觉这么做也不错,比较灵活,可以随时取用。

但是需要把表单内容存到本地客户端,存储在哪里是个问题,我看到CSDN是存储在IndexDB的,毕竟localStorage和sessionStorage存储大小限制比较小。而且会有不同客户端给用户表现不同的体验。把浏览器缓存清了,表现也不同了。不过也的确没有必要把这个临时表单存到服务端。

我没有使用这个方式的原因是:如上述,觉得本地缓存不靠谱;预感需要在业务页面中写许多代码来支持这一功能,如果有很多表单页呢,不太好维护。而我的方式在业务页面中,在需要的时机调用props.setwhen(true)即可。

参考文章:
https://github.com/remix-run/history/issues/690
https://segmentfault.com/a/1190000019230420 =>该文章的最后2段值得好好看

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-10-16 19:33:27  更:2021-10-16 19:33:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 18:31:06-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码