第二篇:React核心架构搭建
Web3项目中的React核心架构搭建实践
在确定技术栈后,接下来就是搭建React应用的核心架构。对于Web3应用来说,架构设计需要特别考虑钱包连接状态、区块链数据获取和状态管理等方面。
项目结构设计
我采用了功能模块化的项目结构:
src/
├── components/ # 通用UI组件
├── contexts/ # React上下文
├── connectors/ # 钱包连接器
├── hooks/ # 自定义Hooks
├── pages/ # 页面组件
├── services/ # 数据服务
├── utils/ # 工具函数
├── constants/ # 常量定义
├── types/ # 类型定义
├── App.tsx # 应用入口组件
└── index.tsx # 渲染入口
入口文件配置
// src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './tailwind.css';
import './index.css';
// 创建根节点并渲染应用
const root = ReactDOM.createRoot(document.getElementById('app') as HTMLElement);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
路由配置
使用React Router v7进行路由管理:
// src/App.tsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import WalletProvider from './contexts/WalletContext';
// 页面组件
import Home from './pages/Home';
import Dashboard from './pages/Dashboard';
import Transaction from './pages/Transaction';
import NotFound from './pages/NotFound';
// 创建MUI主题
const theme = createTheme({
palette: {
primary: {
main: '#3b82f6', // 蓝色主题,适合金融类Web3应用
},
background: {
default: '#f8fafc',
},
},
typography: {
fontFamily: 'Inter, system-ui, sans-serif',
},
});
function App() {
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<WalletProvider>
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/transaction/:id" element={<Transaction />} />
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
</WalletProvider>
</ThemeProvider>
);
}
export default App;
状态管理方案
对于Web3应用,我选择了两种状态管理方案结合使用:
1. Jotai用于原子化状态
// src/store/atoms.ts
import { atom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';
// 钱包连接状态
export const walletConnectedAtom = atom(false);
export const currentAccountAtom = atom('');
export const currentChainIdAtom = atom(1);
// 用户偏好设置(持久化)
export const themeModeAtom = atomWithStorage('themeMode', 'light');
export const recentWalletsAtom = atomWithStorage('recentWallets', []);
2. React Context用于全局状态
// src/contexts/WalletContext.tsx
import { createContext, useContext, useState, useEffect, ReactNode } from 'react';
import { useWeb3React } from '@web3-react/core';
import { ethers } from 'ethers';
import { metaMask } from '../connectors/metaMask';
interface WalletContextType {
account: string | null;
balance: string;
connect: () => Promise<void>;
disconnect: () => Promise<void>;
isLoading: boolean;
}
const WalletContext = createContext<WalletContextType | undefined>(undefined);
export const WalletProvider = ({ children }: { children: ReactNode }) => {
const { account, chainId, active } = useWeb3React();
const [balance, setBalance] = useState('0');
const [isLoading, setIsLoading] = useState(false);
// 获取账户余额
useEffect(() => {
const fetchBalance = async () => {
if (account) {
try {
const provider = new ethers.providers.Web3Provider(window.ethereum);
const balance = await provider.getBalance(account);
setBalance(ethers.utils.formatEther(balance));
} catch (error) {
console.error('Failed to fetch balance:', error);
}
}
};
fetchBalance();
}, [account]);
// 连接钱包
const connect = async () => {
setIsLoading(true);
try {
await metaMask.activate();
} catch (error) {
console.error('Failed to connect:', error);
} finally {
setIsLoading(false);
}
};
// 断开连接
const disconnect = async () => {
setIsLoading(true);
try {
await metaMask.deactivate();
} catch (error) {
console.error('Failed to disconnect:', error);
} finally {
setIsLoading(false);
}
};
return (
<WalletContext.Provider
value={{
account,
balance,
connect,
disconnect,
isLoading,
}}
>
{children}
</WalletContext.Provider>
);
};
export const useWallet = () => {
const context = useContext(WalletContext);
if (context === undefined) {
throw new Error('useWallet must be used within a WalletProvider');
}
return context;
};
自定义Hooks设计
为了提高代码复用性,我设计了一系列自定义Hooks:
// src/hooks/useContract.ts
import { useMemo } from 'react';
import { useWeb3React } from '@web3-react/core';
import { ethers } from 'ethers';
import ERC20ABI from '../abis/ERC20.json';
export const useContract = (address: string, abi: any) => {
const { library, account } = useWeb3React();
const contract = useMemo(() => {
if (!address || !abi || !library) return null;
return new ethers.Contract(address, abi, library.getSigner(account));
}, [address, abi, library, account]);
return contract;
};
export const useERC20Contract = (address: string) => {
return useContract(address, ERC20ABI);
};
架构设计思考
在设计React架构时,我主要考虑了以下几点:
- 状态分层管理:UI状态与Web3状态分离
- 数据获取策略:区块链数据的缓存和更新机制
- 错误处理:钱包连接失败、合约交互异常等情况的处理
- 性能优化:避免不必要的重渲染和区块链请求
- 类型安全:完整的TypeScript类型定义
遇到的挑战
- 钱包连接状态同步:多钱包连接时的状态管理
- 异步操作处理:区块链交互的异步特性需要特别处理
- Provider注入时机:确保Web3 Provider正确注入
总结
React核心架构的搭建是Web3项目的基础,合理的架构设计能够显著提升开发效率和用户体验。在实际开发中,我倾向于采用简洁而不简单的架构,避免过度设计,同时确保代码的可维护性和扩展性。
下一篇,我将分享样式解决方案的集成经验,包括Tailwind CSS与Material-UI的协同使用。
