react实现浏览器的返回、前进、刷新,关闭拦截

刷新和关闭拦截

beforeunload 事件

触发机制: 浏览器的刷新和关闭按钮被点击,点击跳转路由的按钮 (全局生效)
侦听机制: 页面初始化和卸载时侦听事件
缺点:

  • 默认样式,无法改变
  • 全局生效
useEffect(()=>{
        window.addEventListener('beforeunload', beforeunload);
    },[])

    useEffect(()=>{
        window.addEventListener('beforeunload', beforeunload);
    },[])

    const beforeunload=(ev)=>{
        if (ev) {  
               ev.returnValue = '';
            }
    }

返回、前进的弹框拦截

1.react-router-dom中的Prompt组件

触发机制:浏览器的返回和前进按钮被点击(路由发生变化)
缺点: 不能自定义样式

import React from 'react'
import {Prompt } from "react-router-dom"   

export default function StopRoute(){
  const [isOpen,setIsOpen]=useState(false)

  const lanjieReturn=()=>{
  
  (location)=>{
     if(!isOpen) {
       let leave = window.confirm("您确定要离开该页面吗?")         
       if(!leave) {
         return false
       }          
     }else {
      setIsOpen(false)
    /* 返回false是拦截的关键 */
     return false
     }
   }

  }

  return (
    <>
        /* message是用来显示内容的   when是用来控制拦截的时机的(也就是弹框出现的时机) */
        <Prompt message="您确定要离开该页面吗?" when={isOpen} />       //第一种用法   message直接写字符串
        
         <Prompt                                                               //第二种用法     message是函数,但是函数内部只能是三木运算,且不用return   (其实质也就是用法一的直接写字符串)
           message = {() => {
           this.state.isOpen? false: "您确定要离开该页面吗?"
         }}

        <Prompt    message = {lanjieReture}/>  //第三种用法   mesage是函数,并且不是三目运算,则函数必须返回false,并且不能自定义弹框,因为react的显示必须写在render里面,这里的函数rentuen的是false,不是DOM,所以只能只用js的原生弹框来拦截

    </>
  )
}

2.useHistory().block

触发机制: 浏览器的返回和前进按钮被点击
缺点:

  • 用到的页面,其组件在路由或者在任何地方不能使用高阶组件(也就是不能再被别的组件包裹),否则失效
  • 如果用到的页面不是首页,那么进来的页面最好使用push,不能使用replace,否则不好使
import React, {useEffect, useState} from 'react';
import {useHistory} from 'react-router';
import Dialog from '@/components/Dialog'

export default function UserConfirmationTwo(props) {
    const {when = false} = props;
    const [isShowModal, setIsShowModal] = useState(false);
    const history = useHistory();
    const [nextLocation, setNextLocation] = useState(null);
    const [action, setAction] = useState();
    const [unblock, setUnblock] = useState(null);

    useEffect(() => {
        if (!when || unblock) {
            return;
        }
        const cancel = history.block((nextLocation, action) => {
            if (when) {
                setIsShowModal(true);
            }
            setNextLocation(nextLocation);
            setAction(action);
            return false;
        });
        setUnblock(() => {
            return cancel;
        });
    }, [when, unblock]);

    useEffect(() => {
        return () => {
            unblock && unblock();
        };
    }, []);

    function onConfirm() {
        unblock && unblock();
        if (action === 'PUSH') {
            history.push(nextLocation);
        } else if (action === 'POP') {
            history.goBack();
        } else if (action === 'REPLACE') {
            history.replace(nextLocation);
        }
        props.closeStopRouteDialog(1);
        setIsShowModal(false);
 
    }

    function onCancel() {
        props.closeStopRouteDialog(0);    //点击取消和确定的事件
        setIsShowModal(false);
    }

    return (
        <>
            {isShowModal && <Modal
                title="是否返回?"
                width='450'
                visible={isShowModal}
                confirm={()=>onConfirm()}
                close={onCancel}
            >
            </Modal>}
        </>
    );
}

3.pushState popState

触发机制:浏览器的返回和前进按钮被点击
缺点: 没缺点了,js原生的

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        *{
            margin:0;
            padding:0;
        }
        .menu{
             100px;
            height: 40px;
            font-size: 30px;
            line-height: 40px;
            text-align: center;
            border:1px solid #000000;
            float: left;
            margin-left: 50px;
            user-select: none;
        }
        p{
            clear: both;
            display: none;
        }
        .dialog{
             100%;
            height: 100%;
            background: black;
            opacity: 0.5;
            position: absolute;
            top: 0;
            left: 0;
            display: none;
        }
        .container{
             500px;
            height: 200px;
            position: absolute;
            z-index: 100;
            left:calc(50% - 250px);
            top:calc(50% - 100px);
            background: #FFFFFF;
            padding:24px;
            display: flex;
            flex-direction: column;
            align-items: start;
            justify-content: space-between;
        }
        .title{
            color:#000000;
            font-size: 30px;
            line-height: 30px;
            font-weight: 600;;
        }
        .btn{
            
        }
        .btnLeft{
             50px;
            height: 30px;
            background-color:red;
            margin-right:395px;;
        }
        .btnRight{
             50px;
            height: 30px;
            background-color:chartreuse
        }
       
    </style>
</head>
<body>
    <div class='menu'>水果</div>
    <div class='menu'>蔬菜</div>
    <div class='menu'>零食</div>
    <div class='menu'>饮料</div>
    <br>
    <br>
    <p>猕猴桃
        苹果
        梨</p>
    <p>白菜
        土豆
        地瓜</p>
    <p>辣条
        牛肉干
        薯片</p>
    <p>可乐
        雪碧
        果汁</p>


    <div class='dialog'>
        <div class='container'>
            <div class='title'>确定要返回吗</div>
            <div class="btn">
                <button class='btnLeft'>取消</button>
                <button class='btnRight'>确定</button>
            </div>
        </div>
    </div>


    <script>
        var arr,divs;
        let dialog=document.querySelector('.dialog')
        init()
        function init(){
            // 当历史前进或者后退时就会收到这个事件
            window.onpopstate=popStateHandler;
            arr=Array.from(document.getElementsByTagName("p"));
            divs=Array.from(document.querySelectorAll(".menu"));
            cancel=document.querySelector('.btnLeft')
            ensure=document.querySelector('.btnRight')
            arr[0].style.display="block";
            for(var i=0;i<divs.length;i++){
                divs[i].onclick=clickHandler;
            }
            cancel.addEventListener('click',handleClick)
            ensure.addEventListener('click',handleClick)
            
        }


        function clickHandler(){
           var index=divs.indexOf(this);
        //    history.pushState({state:1},"","#"+this.innerHTML);
            // 在历史记录列表中增加数据,后面的#内容标示当前跳转部分
             history.pushState({index:index}, "", document.URL);
             changeMenu(index);
        }

        function popStateHandler(){
            console.log(history.state);
            // changeMenu(history.state.index)
            dialog.style.display='block'
        }

       function changeMenu(index){
        for(var i=0;i<arr.length;i++){
                if(i===index){
                    arr[i].style.display="block";
                }else{
                    arr[i].style.display="none";
                }
            }
       }

       function handleClick(e){
        if(e.target.innerHTML==='确定'){
            dialog.style.display='none'
            console.log('点击了确定')
            location.href='http://www.baidu.com'
            return ;
        }
        dialog.style.display='none'
        console.log('点击了取消')
        // window.location.href='http://www.baidu.com'
       }
    </script>
</body>
</html>
原文地址:https://www.cnblogs.com/94-Lucky/p/14828661.html