如何使用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 的源代码和文档:
- 源代码https://github.com/NoahZinsmeister/web3-react/tree/v6
- 文档https://github.com/NoahZinsmeister/web3-react/tree/v6/docs
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
1.2 用作src
我们的源代码目录:
mkdir src
mv pages src/pages
mv styles src/styles
编辑tsconfig.json
,添加:
"baseUrl": "./src",
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
1.4 运行项目并查看:http://localhost:3000/
yarn dev
1.5 添加依赖项
我们将在这里使用Ethers.js
、web3-react
等。
yarn add ethers
yarn add @web3-react/core @web3-react/injected-connector
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
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
我们该怎么做?我们添加一个ConnectWallet
组件来显示账户并连接到钱包。
-
点击连接钱包按钮,勾选
activate(injectedConnector)
后useWeb3React
即被调用。 -
连接后,显示
chainId
和account
。
运行,我们可以在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>
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 />}
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
}, [])
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",
运行命令安装依赖项:
yarn install
步骤 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>
)
}
一些解释:
-
用于显示 ENS/地址和账户以太币余额的组件
-
使用
getBalance
web3 提供程序的功能查询以太币余额
步骤 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>
)
}
一些解释:
- 三个组件:MetaMaskCard、Connect、Status
- 连接组件提供一个按钮,用户可以点击连接 MetaMask 钱包,方法是调用
metaMask.activate()
- 状态组件显示状态根据
isActivating
和isActive
我们在这里得到所有需要的钩子:
const [metaMask, hooks] = initializeConnector<MetaMask>((actions) => new MetaMask(actions))
const { useChainId, useAccounts, useError, useIsActivating, useIsActive, useProvider, useENSNames } = hooks
步骤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
运行命令yarn dev
并访问示例应用程序:localhost:3000
总而言之,web3-react 提供了一个便捷的工具,它提供了 React 和其他编程语言之间的上下文和钩子。尽情享受吧。
鏂囩珷鏉ユ簮锛�https://dev.to/yakult/how-to-use-web3-react-to-develop-dapp-1cgn