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

React中Context存储的钱包地址首次跳转后变为null的问题排查

React Context中web3钱包地址首次路由跳转后丢失的问题

问题描述

在React项目中,通过Context管理web3钱包地址状态,使用react-router-dom实现路由。用户首次连接钱包后跳转至/explore页面时,Context内的walletAddress状态会短暂存在后变为null,导致依赖该状态的UI组件异常;但**断开钱包后重新连接(不刷新页面)**时,应用功能恢复正常。已尝试将Context Provider包裹整个App组件,问题仍未解决。

相关代码

1. WalletContext.js

import { createContext, useState, useEffect } from 'react';
import { ethers } from 'ethers';

export const WalletContext = createContext();

export const WalletProvider = ({ children }) => {
  const [walletAddress, setWalletAddress] = useState(null);

  const connectWallet = async () => {
    if (!window.ethereum) return alert('请安装MetaMask');
    
    try {
      const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
      setWalletAddress(accounts[0]);
    } catch (err) {
      console.error('连接钱包失败:', err);
    }
  };

  const disconnectWallet = () => {
    setWalletAddress(null);
  };

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

    const handleAccountsChanged = (accounts) => {
      if (accounts.length > 0) {
        setWalletAddress(accounts[0]);
      } else {
        setWalletAddress(null);
      }
    };

    window.ethereum.on('accountsChanged', handleAccountsChanged);

    return () => {
      window.ethereum.removeListener('accountsChanged', handleAccountsChanged);
    };
  }, []);

  return (
    <WalletContext.Provider value={{ walletAddress, connectWallet, disconnectWallet }}>
      {children}
    </WalletContext.Provider>
  );
};

2. App.jsx

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { WalletProvider } from './contexts/WalletContext';
import Home from './pages/Home';
import Explore from './pages/Explore';
import Navbar from './components/Navbar';

function App() {
  return (
    <WalletProvider>
      <Router>
        <Navbar />
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/explore" element={<Explore />} />
        </Routes>
      </Router>
    </WalletProvider>
  );
}

export default App;

3. Home.jsx(连接钱包并跳转页面)

import { useContext } from 'react';
import { useNavigate } from 'react-router-dom';
import { WalletContext } from '../contexts/WalletContext';

const Home = () => {
  const { connectWallet } = useContext(WalletContext);
  const navigate = useNavigate();

  const handleConnectAndNavigate = async () => {
    await connectWallet();
    navigate('/explore');
  };

  return (
    <div>
      <button onClick={handleConnectAndNavigate}>连接钱包并进入探索页面</button>
    </div>
  );
};

export default Home;

4. Explore.jsx(依赖walletAddress的页面)

import { useContext } from 'react';
import { WalletContext } from '../contexts/WalletContext';

const Explore = () => {
  const { walletAddress } = useContext(WalletContext);

  if (!walletAddress) {
    return <div>请先连接钱包</div>;
  }

  return (
    <div>
      <h2>探索页面</h2>
      <p>当前钱包地址: {walletAddress}</p>
    </div>
  );
};

export default Explore;

问题分析与解决方案

1. 异步状态更新与路由跳转的时序冲突

首次连接时,connectWallet的异步操作完成后立即执行navigate,但React的状态更新是异步的——此时walletAddress的新值还未同步到Context中,跳转后Explore组件先读取到旧的null值;后续accountsChanged事件触发时,可能因为路由跳转导致组件挂载时机问题,意外将状态重置。

修复方案:等待状态更新完成后再跳转

修改Home组件,通过监听walletAddress状态变化来触发跳转:

import { useContext, useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { WalletContext } from '../contexts/WalletContext';

const Home = () => {
  const { connectWallet, walletAddress } = useContext(WalletContext);
  const navigate = useNavigate();
  const [triggerNavigate, setTriggerNavigate] = useState(false);

  const handleConnect = async () => {
    await connectWallet();
    setTriggerNavigate(true);
  };

  useEffect(() => {
    if (triggerNavigate && walletAddress) {
      navigate('/explore');
      setTriggerNavigate(false);
    }
  }, [triggerNavigate, walletAddress, navigate]);

  return (
    <div>
      <button onClick={handleConnect}>连接钱包并进入探索页面</button>
    </div>
  );
};

export default Home;

2. 初始化时未读取已连接的钱包地址

首次页面加载时,Context没有主动读取钱包当前的已连接地址,仅依赖accountsChanged事件更新状态,导致首次连接后跳转的间隙状态未同步。

修复方案:在Provider初始化时主动读取地址

修改WalletProvider的初始化逻辑:

export const WalletProvider = ({ children }) => {
  const [walletAddress, setWalletAddress] = useState(null);

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

    // 初始化时读取已连接的地址
    const fetchCurrentAddress = async () => {
      const accounts = await window.ethereum.request({ method: 'eth_accounts' });
      if (accounts.length > 0) {
        setWalletAddress(accounts[0]);
      }
    };

    fetchCurrentAddress();

    const handleAccountsChanged = (accounts) => {
      setWalletAddress(accounts.length > 0 ? accounts[0] : null);
    };

    window.ethereum.on('accountsChanged', handleAccountsChanged);

    return () => {
      window.ethereum.removeListener('accountsChanged', handleAccountsChanged);
    };
  }, []);

  // ...其余connectWallet、disconnectWallet逻辑不变
};

3. 检查Context实例唯一性

确保所有组件导入的是同一个WalletContext实例,避免因复制代码导致多个createContext调用,造成状态隔离。

验证步骤

  1. 在setWalletAddress和Explore组件中添加console.log,观察状态变化的时序;
  2. 检查accountsChanged事件在首次连接时的触发时机,确认是否存在重复更新状态的情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 20:45:55