如何使用Web3-React开发DApp

2025-06-10

如何使用Web3-React开发DApp

Web3-react 是一个在区块链、DApp 和 web3 开发中常用的库。它在 Github 上拥有超过 2800 个 star,并被超过 10000 个 repo 使用。

注意:如果您已经知道web3-react,您可以直接转到第 1 部分获取操作指南。

最后更新:2022年1月30日


0. 什么是web3-react

在开始旅程之前,我将概述我们在这里使用的堆栈和技术:

  • 前端:React、Next.js、TypeScript

  • 区块链 API:Ethers.js

  • 开发环境:Hardat、MetaMask、以太坊测试网

web3-react是一个用于构建现代以太坊 DApp 的 React/Ethers.js 框架,由 Uniswap 工程主管 Noah Zinsmeister 开发。它工作在前端和区块链 API 之间。

从高层次上讲,web3-react 是一个状态机,它确保与您的 dApp 相关的某些关键数据(例如用户的当前帐户)保持最新。

为此,web3-react 使用 Context 来有效地存储这些数据,并将其注入到应用程序中需要的任何位置。(来自web3-react v6 文档

它是一个 React 库,底层区块链 API 是Ethers.js。稳定版本是 v6,目前 v8 处于测试阶段。web3-react代码库位于:https://github.com/NoahZinsmeister/web3-react

由于它的使用非常广泛,我惊讶地发现相关的文档非常少。目前为止,我能参考的只有4篇文档:

  • web3-react 源代码(v6 和 v8 beta)
  • 包中的示例(v6 和 v8 beta)
  • web3-react v6 文档
  • Lorenzo Sicilia 在 Consensys 博客上发表的教程

从 v6 到 v8 beta 版本,主要有一些变化。一些常用的 hooksuseWeb3React被标记为“仅用于向后兼容”。(Gerhard Steenkamp 和 William Schwab 在社区中为 web3-react 撰写了一份优秀的文档)

我可以预见,v8web3-react将会被广泛使用,因为它能够满足日益增长的 web3 需求。我写了这篇教程来帮助开发者使用它,包括 v6 和 v8(beta)。

有用的链接:web3-react v6 的源代码和文档:

Lorenzo 的 Web3-React 教程对本指南很有帮助,我从中复制了示例代码。非常感谢。这个教程很棒,因为它演示了在区块链和 DApp 之间同步数据的策略:

“如果您想使用以太坊 dapp 简化数据获取策略,SWR 和 Ether.js 是两个不错的库。”

有多个库,其中一些的比较可以在WAGMI 文档中找到。

  • Web3-React
  • 瓦格米
  • 使用DApp
  • web3modal
  • Web3-UI
  • Web3UI
  • Scaffold-eth / eth-components / eth-hooks

1. 设置 playground:创建一个 Next.js 项目

首先,我们启动一个 Next.js 项目,使用 编写 DAPP web3-react。然后,我们添加依赖项。请遵循以下 6 个步骤:

1.1 创建项目:



yarn create next-app playeth --typescript
cd playeth


Enter fullscreen mode Exit fullscreen mode

1.2 用作src我们的源代码目录:



mkdir src
mv pages src/pages
mv styles src/styles


Enter fullscreen mode Exit fullscreen mode

编辑tsconfig.json,添加:



    "baseUrl": "./src",


Enter fullscreen mode Exit fullscreen mode

1.3 清除index.tsx

使index.tsx简单化。



import type { NextPage } from 'next'
import styles from '../styles/Home.module.css'

const Home: NextPage = () => {
  return (
    <div>
      <main className={styles.main}>
        <h2>
          Welcome to playground
        </h2>
      </main>
    </div>
  )
}
export default Home


Enter fullscreen mode Exit fullscreen mode

1.4 运行项目并查看:http://localhost:3000/



yarn dev


Enter fullscreen mode Exit fullscreen mode

1.5 添加依赖项

我们将在这里使用Ethers.jsweb3-react等。



yarn add ethers
yarn add  @web3-react/core  @web3-react/injected-connector


Enter fullscreen mode Exit fullscreen mode

web3-react安装的是稳定版本 6.xx

如果您只想添加Ethers.js使用的组件,您可以将第一个命令替换为:yarn add @ethersproject/providers


2. 使用 Web3-react、ethers 和 MetaMask 连接到区块链

DApp(web3 应用)与传统 Web 应用的主要区别在于,DApp 连接到区块链而不是中心化服务器,以实现 1)用户登录和授权、2)有关数据的数据和 3)DeFi、NFT、游戏、DAO 治理等功能。

当 DApp 在桌面浏览器中使用时,可以通过以下三个方式实现:

  • 用户端的MaskMask钱包

  • 浏览器/服务器和区块链端点之间的 Ethers.js

  • 服务器/浏览器上的 React 和 Web3-React

让我们开始用 创建一个 DApp web3-react。我们将使用它的两个部分:

  • Web3ReactProvider,上下文
  • useWeb3React,钩子

请注意,从 v6 到 v8 有一个很大的升级。useWeb3React标有注释“仅用于向后兼容”。

2.1 添加提供商_app.tsx

<Web3ReactProvider>添加上下文提供程序_app.tsx



import '../styles/globals.css'
import type { AppProps } from 'next/app'

import { Web3ReactProvider } from '@web3-react/core'
import { Web3Provider } from '@ethersproject/providers'

function getLibrary(provider: any): Web3Provider {
  const library = new Web3Provider(provider)
  library.pollingInterval = 12000
  return library
}

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <Web3ReactProvider getLibrary={getLibrary}>
      <Component {...pageProps} />
    </Web3ReactProvider>
  )
}

export default MyApp



Enter fullscreen mode Exit fullscreen mode

2.2 编辑index.tsx

用于useWeb3React通过 MetaMask 注入的提供程序连接到区块链。您可以直接通过 调用它windows.ethereum。MetaMask 以太坊提供程序 API 文档在此处

您需要在 Chrome 浏览器中安装 MetaMask 扩展程序。

编辑index.tsx以提供一个按钮,使用 MetaMask 钱包及其注入浏览器的区块链提供商来连接区块链。



import type { NextPage } from 'next'
import styles from '../styles/Home.module.css'
import { useEffect } from 'react'

import { useWeb3React } from '@web3-react/core'
import { Web3Provider } from '@ethersproject/providers'
import { InjectedConnector } from '@web3-react/injected-connector'

const ConnectWallet = () => {
  const injectedConnector = new InjectedConnector({supportedChainIds: [1,3, 4, 5, 42, ],})
  const { chainId, account, activate, active,library } = useWeb3React<Web3Provider>()
  const onClick = () => {
    activate(injectedConnector)
  }

  useEffect(() => {
    console.log(chainId, account, active)
    },);

  return (
    <div>
      <div>ChainId: {chainId}</div>
      <div>Account: {account}</div>
      {active ? (
        <div> </div>
      ) : (
        <button type="button" onClick={onClick}>
          Connect Connect
        </button>
      )}
    </div>
  )
}

const Home: NextPage = () => {
  return (
    <div >
      <main className={styles.main}>
        <h2 >Welcome to playground</h2>
        <ConnectWallet />
      </main>
    </div>
  )
}

export default Home


Enter fullscreen mode Exit fullscreen mode

我们该怎么做?我们添加一个ConnectWallet组件来显示账户并连接到钱包。

  • 点击连接钱包按钮,勾选activate(injectedConnector)useWeb3React即被调用。

  • 连接后,显示chainIdaccount

运行,我们可以在http://localhost:3000/yarn dev玩这个简单的 DApp

图片描述

注意:如果您想真正断开钱包与此页面的连接,请断开与 MetaMask 的连接。


3. 从以太坊主网获取数据:读取区块链

在第二部分中,我们建立了与区块链交互的环境。在本节中,我们将尝试从以太坊主网获取数据。

3.1 使用 useState 和 useEffect 查询数据并展示

进行一些更改index.tsx,请注意,这是一个快速而粗糙的代码片段,仅用于说明。



  const [balance,setBalance]= useState("")
...
  useEffect(() => {
    library?.getBalance(account).then((result)=>{
      setBalance(result/1e18)
    })
    },);
...
      <div>Balance: {balance}</div>


Enter fullscreen mode Exit fullscreen mode

3.2 使用SWR查询数据

SWR ( https://swr.now.sh/ ) 的意思是“Stale-While-Revalidate”。Lorenzo的 Web3-React 教程建议使用此策略。以下是一段关于 SWR 的引言:

SWR 首先从缓存中返回数据(stale),然后发送获取请求(revalidate),最后再次返回最新数据。

安装后swr,编辑index.tsx



import useSWR from 'swr'
...
    const fetcher = (library) => (...args) => {
      const [method, ...params] = args
      console.log("fetcher",method, params)
      const result = library[method](...params)
     return library[method](...params)
    }

    const BalanceSWR  = () => {
      const { account, library } = useWeb3React<Web3Provider>()
      const { data: balance } = useSWR(['getBalance', account, 'latest'], {
        fetcher: fetcher(library),
      })
      console.log(balance)
      if(!balance) {
        return <div>...</div>
      }
      return <div>Balance: Ξ {balance/1e18}</div>
    }  
...
      {active && <BalanceSWR />}


Enter fullscreen mode Exit fullscreen mode

Lorenzo 的 Web3-React 教程解释道:

如你所见,这是一个部分应用函数。这样,​​我可以在配置 fetcher 时注入库(我的 Web3Provider)。之后,每次键发生变化时,都可以通过返回所需的 Promise 来解析该函数​​。

3.3 实时更新数据

使用 SWR 的优点是它可以实时更新数据。

我们将遵循 Lorenzo 的 Web3-React 教程来完成此操作。

所用功能是SWR的mutate功能。

  • 监听以太坊区块号变化:ethers.provider.on()
  • 使用 SWR mutate 触发刷新

编辑BalanceSWR组件index.tsx



      const { account, library } = useWeb3React<Web3Provider>()
      const { data: balance,mutate } = useSWR(['getBalance', account, 'latest'], {
        fetcher: fetcher(library),
      })

      useEffect(() => {
        // listen for changes on an Ethereum address
        console.log(`listening for blocks...`)
        library.on('block', () => {
          console.log('update balance...')
          mutate(undefined, true)
        })
        // remove listener when the component is unmounted
        return () => {
          library.removeAllListeners('block')
        }
        // trigger the effect only on component mount
      }, [])


Enter fullscreen mode Exit fullscreen mode

Lorenzo 的教程有更多关于如何与智能合约交互以及如何监听智能合约事件的内容。您可以继续阅读并进行实验:https://consensys.net/blog/developers/how-to-fetch-and-update-data-from-ethereum-with-react-and-swr/

我们将转到 Web3-react 版本 8 来查看有哪些变化。


4. 深入研究 Web3-react 版本 8

这里有一个例子来演示如何使用它。我们将直接从中截取一些代码片段。你可以在 packages/example/ 找到这个例子。

步骤 1 创建示例项目

创建一个 next.js 项目,就像我们在第 1 部分中所做的那样。

编辑 package.json 以添加依赖项:



    "@ethersproject/bignumber": "^5.4.2",
    "@ethersproject/experimental": "^5.5.0",
    "@ethersproject/providers": "^5.5.1",
    "@ethersproject/units": "^5.4.0",
    "@walletconnect/ethereum-provider": "^1.7.1",
    "@web3-react/core": "8.0.6-beta.0",
    "@web3-react/eip1193": "8.0.2-beta.0",
    "@web3-react/empty": "8.0.2-beta.0",
    "@web3-react/metamask": "8.0.3-beta.0",
    "@web3-react/network": "8.0.2-beta.0",
    "@web3-react/types": "8.0.2-beta.0",
    "@web3-react/url": "8.0.2-beta.0",
    "@web3-react/walletconnect": "8.0.4-beta.0",
    "@web3-react/walletlink": "8.0.4-beta.0",


Enter fullscreen mode Exit fullscreen mode

运行命令安装依赖项:



yarn install


Enter fullscreen mode Exit fullscreen mode

步骤 2:AccountsComponent

components/AccountsComponent.tsx基于以下创建: https://github.com/NoahZinsmeister/web3-react/blob/main/packages/example/components/Accounts.tsx



import type { BigNumber } from '@ethersproject/bignumber'
import { formatEther } from '@ethersproject/units'
import type { Web3ReactHooks } from '@web3-react/core'
import { useEffect, useState } from 'react'

function useBalances(
  provider?: ReturnType<Web3ReactHooks['useProvider']>,
  accounts?: string[]
): BigNumber[] | undefined {
  const [balances, setBalances] = useState<BigNumber[] | undefined>()

  useEffect(() => {
    if (provider && accounts?.length) {
      let stale = false

      void Promise.all(accounts.map((account) => provider.getBalance(account))).then((balances) => {
        if (!stale) {
          setBalances(balances)
        }
      })

      return () => {
        stale = true
        setBalances(undefined)
      }
    }
  }, [provider, accounts])

  return balances
}

export function AccountsComponent({
  accounts,
  provider,
  ENSNames,
}: {
  accounts: ReturnType<Web3ReactHooks['useAccounts']>
  provider: ReturnType<Web3ReactHooks['useProvider']>
  ENSNames: ReturnType<Web3ReactHooks['useENSNames']>
}) {
  const balances = useBalances(provider, accounts)

  if (accounts === undefined) return null

  return (
    <div>
      Accounts:{' '}
      <b>
        {accounts.length === 0
          ? 'None'
          : accounts?.map((account, i) => (
              <ul key={account} style={{ margin: 0, overflow: 'hidden', textOverflow: 'ellipsis' }}>
                <li>{ENSNames?.[i] ?? account}</li>
                <li>{balances?.[i] ? ` (Ξ${formatEther(balances[i])})` : null}</li>
              </ul>
            ))}
      </b>
    </div>
  )
}



Enter fullscreen mode Exit fullscreen mode

一些解释:

  • 用于显示 ENS/地址和账户以太币余额的组件

  • 使用getBalanceweb3 提供程序的功能查询以太币余额

步骤 3:MetaMaskCard

创建components/MetaMaskCard.tsx。MetaMaskCard 基于:https://github.com/NoahZinsmeister/web3-react/tree/main/packages/example/components中的 MetaMaskCard、Connect、Status 组件



import type { Web3ReactHooks } from '@web3-react/core'
import { AccountsComponent } from './AccountsComponent'
import { initializeConnector } from '@web3-react/core'
import { MetaMask } from '@web3-react/metamask'

const [metaMask, hooks] = initializeConnector<MetaMask>((actions) => new MetaMask(actions))
const {  useChainId, useAccounts, useError, useIsActivating, useIsActive, useProvider, useENSNames } = hooks

function Connect({
  isActivating,
  error,
  isActive,
}: {
  chainId: ReturnType<Web3ReactHooks['useChainId']>
  isActivating: ReturnType<Web3ReactHooks['useIsActivating']>
  error: ReturnType<Web3ReactHooks['useError']>
  isActive: ReturnType<Web3ReactHooks['useIsActive']>
}) {

  if (error) {
    return (
      <div style={{ display: 'flex', flexDirection: 'column' }}>
        <button
          onClick={() => metaMask.activate()}
        >
          Try Again?
        </button>
      </div>
    )
  } else if (isActive) {
    return (
      <div style={{ display: 'flex', flexDirection: 'column' }}>
        <button onClick={() => metaMask.deactivate()}>Disconnect</button>
      </div>
    )
  } else {
    return (
      <div style={{ display: 'flex', flexDirection: 'column' }}>
        <button
          onClick={
            isActivating
              ? undefined
              : () => metaMask.activate()
          }
          disabled={isActivating}
        >
          Connect
        </button>
      </div>
    )
  }
}

function Status({
  isActivating,
  error,
  isActive,
}: {
  isActivating: ReturnType<Web3ReactHooks['useIsActivating']>
  error: ReturnType<Web3ReactHooks['useError']>
  isActive: ReturnType<Web3ReactHooks['useIsActive']>
}) {
  return (
    <div>
      {error ? (
        <>
          🔴 {error.name ?? 'Error'}
          {error.message ? `: ${error.message}` : null}
        </>
      ) : isActivating ? (
        <>🟡 Connecting</>
      ) : isActive ? (
        <>🟢 Connected</>
      ) : (
        <>⚪️ Disconnected</>
      )}
    </div>
  )
}

export default function MetaMaskCard() {
  const chainId = useChainId()
  const accounts = useAccounts()
  const error = useError()
  const isActivating = useIsActivating()

  const isActive = useIsActive()

  const provider = useProvider()
  const ENSNames = useENSNames(provider)

  return (
      <div style={{border: '1px solid'}}>
        <b>MetaMask</b>
        <Status isActivating={isActivating} error={error} isActive={isActive} />
        <AccountsComponent accounts={accounts} provider={provider} ENSNames={ENSNames} />
        <Connect chainId={chainId} isActivating={isActivating} error={error} isActive={isActive} />
      </div>

  )
}



Enter fullscreen mode Exit fullscreen mode

一些解释:

  • 三个组件:MetaMaskCard、Connect、Status
  • 连接组件提供一个按钮,用户可以点击连接 MetaMask 钱包,方法是调用metaMask.activate()
  • 状态组件显示状态根据isActivatingisActive

我们在这里得到所有需要的钩子:



const [metaMask, hooks] = initializeConnector<MetaMask>((actions) => new MetaMask(actions))
const {  useChainId, useAccounts, useError, useIsActivating, useIsActive, useProvider, useENSNames } = hooks


Enter fullscreen mode Exit fullscreen mode

步骤4:index.tsx

使用 Next.js 动态导入来导入 MetaMaskCard。



import type { NextPage } from 'next'
import styles from 'styles/Home.module.css'

import dynamic from 'next/dynamic'

const MetaMaskCard = dynamic(() => import('../components/MetaMaskCard'), { ssr: false })

const Home: NextPage = () => {
  return (
    <div>
      <main  className={styles.main}>
        <h2>
          Welcome to playground
        </h2>
        <MetaMaskCard />
      </main>
    </div>
  )
}
export default Home


Enter fullscreen mode Exit fullscreen mode

运行命令yarn dev并访问示例应用程序:localhost:3000


总而言之,web3-react 提供了一个便捷的工具,它提供了 React 和其他编程语言之间的上下文和钩子。尽情享受吧。

鏂囩珷鏉ユ簮锛�https://dev.to/yakult/how-to-use-web3-react-to-develop-dapp-1cgn
PREV
Web3 教程:构建类似 OpenSea 的 NFT 市场 DApp
NEXT
图像优化、处理和托管