You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Chrome中关闭MetaMask登录弹窗未抛出4001错误,Edge正常

问题分析

这是MetaMask在Chrome浏览器下的状态同步异常问题:

  • 用户关闭登录弹窗后,MetaMask未正确触发eth_requestAccounts的Promise拒绝回调(不抛出4001错误),导致前端代码的catch块无法执行,按钮一直处于禁用状态。
  • MetaMask后台保留了未完成的请求状态,刷新页面无法清除(扩展状态独立于页面),必须重启浏览器才能重置。
  • Edge浏览器的状态同步逻辑与Chrome不同,因此不会出现残留通知问题。
解决方案

针对代码核心问题(未处理Promise pending状态+重复请求),可通过以下方式修复:

1. 添加请求状态锁+超时处理

新增请求状态跟踪避免重复调用,同时给请求设置超时强制重置状态:

import { Card, CardContent, Button, Grid } from '@material-ui/core'
import React, { useEffect, useState } from "react";
import MetaMaskOnboarding from '@metamask/onboarding';

function MetaMaskConnectButton(props) {    
    const forwarderOrigin = 'http://localhost:3000';    
    const onboarding = new MetaMaskOnboarding({ forwarderOrigin });    
    const { ethereum } = window;    
    const [buttonText, setButtonText] = useState('Connect MetaMask')    
    const [isButtonDisabled, setIsButtonDisabled] = useState(false)    
    const [mmAccounts, setMmAccounts] = useState('')
    // 新增:跟踪请求是否正在进行
    const [isRequestPending, setIsRequestPending] = useState(false)    

    useEffect(() => {        
        if (!isMetaMaskInstalled()) {            
            setButtonText('Click here to install MetaMask!')        
        }
    }, [])    

    useEffect(() => {        
        if (mmAccounts == null || mmAccounts.length === 0)
            setIsButtonDisabled(false)          
        else            
            setIsButtonDisabled(true)    
    }, [mmAccounts])    

    async function metaMaskClientCheck() {        
        if (isRequestPending) return; // 请求进行中直接返回,避免重复触发
        if (!isMetaMaskInstalled()) {            
            await metamaskInstall()            
            setIsButtonDisabled(true)        
        } 
        else {            
            await metamaskConnect()            
        }    
    }    

    const isMetaMaskInstalled = () => {        
          return Boolean(ethereum && ethereum.isMetaMask);    
    }    

    const metamaskInstall = async () => {        
        setButtonText('Onboarding in progress...')        
        setIsButtonDisabled(true)        
        onboarding.startOnboarding();    
    }    

    const metamaskConnect = async () => {        
        setIsRequestPending(true)
        setIsButtonDisabled(true)        
        setButtonText('Connecting to MetaMask...')        
        try {            
            // 15秒超时处理,避免Promise永久pending
            const accounts = await Promise.race([
                ethereum.request({ method: 'eth_requestAccounts' }),
                new Promise((_, reject) => setTimeout(() => reject(new Error('Request timed out')), 15000))
            ])
            console.log('Ethereum Request over')            
            let account = accounts[0]            
            setMmAccounts(account)            
            setButtonText('MetaMask Connected')        
        } catch (error) {            
            console.error(error);            
            // 统一处理用户拒绝或超时情况
            setButtonText(error.code === 4001 ? 'Connect MetaMask' : 'Connection failed')
            setIsButtonDisabled(false)        
        } finally {
            // 无论成功失败,重置请求状态
            setIsRequestPending(false)
        }    
    };    

    return (        
        <Grid container xs={12} sm={12}>
            <Grid container xs={6} sm={6} justify='flex-start'>
                <Button                    
                    disabled={isButtonDisabled}                    
                    variant='contained'                    
                    color='secondary'
                    className='button-blue originalText'
                    onClick={() => metaMaskClientCheck()}
                    classes={{ root: 'ng-button-filled-secondary' }}
                >
                    {buttonText}
                </Button>
            </Grid>
        </Grid>
    )}
export default MetaMaskConnectButton

2. 监听MetaMask状态事件(可选增强)

添加账户变更和断开事件监听,确保前端状态与钱包同步:

useEffect(() => {
    if (!ethereum) return;

    // 监听账户变更
    const handleAccountsChanged = (accounts) => {
        if (accounts.length > 0) {
            setMmAccounts(accounts[0])
            setButtonText('MetaMask Connected')
        } else {
            setMmAccounts('')
            setButtonText('Connect MetaMask')
        }
    }

    // 监听断开连接
    const handleDisconnect = () => {
        setMmAccounts('')
        setButtonText('Connect MetaMask')
        setIsButtonDisabled(false)
    }

    ethereum.on('accountsChanged', handleAccountsChanged)
    ethereum.on('disconnect', handleDisconnect)

    return () => {
        ethereum.removeListener('accountsChanged', handleAccountsChanged)
        ethereum.removeListener('disconnect', handleDisconnect)
    }
}, [ethereum])
关键说明
  • Promise.race处理用户长时间不操作或关闭弹窗的情况,避免Promise永久pending。
  • isRequestPending标志位防止重复触发请求,解决"Already processing"错误。
  • 事件监听确保前端状态与MetaMask钱包状态同步,减少不同步问题。

内容的提问来源于stack exchange,提问作者Cheryl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 07:14:56