使用 React 和 Network.js 构建去中心化应用前端

使用 React Network.js 构建可通过 Infura Metamask 接入以太坊主网并请求数据的去中心化应用前端

由于区块链操作的复杂性以及许多罕见的用户体验/用户界面问题需要解决,在以太坊上开发服务或业务十分困难。本系列指南主要以前端解决方案为中心,旨在帮助您快速开始进行以太坊去中心化应用开发。请放心,您无需重新设计区块链。您可以利用许多现成的工具和服务来快速构建可运行的去中心化应用,包括:

综合使用这些产品可以取代数据库设置和用户凭证管理,从而构建快速启动的区块链基础设施。在本教程中,我们将使用 Network JS 构建一个 React 去中心化应用,它可以与 MetaMask 交互,使用 Infura 作为以太坊主网连接。

环境设置

在开始之前,要求具有安装了 npm 的 Node.js。我们首先来创建一个新项目…

mkdir web3-infura && cd web3-infura

npm init -y

…然后安装 OpenZeppelin 的 Network JS,以获取适用于 web3 的代码库方法:

npm install @openzeppelin/network

我们现在可以开始创建去中心化应用

我们使用一个名为 create-react-app 的常用 React 样板创建去中心化应用的初始版本。使用 npx(npm 的包运行器)进行设置非常简单。只需一行便能得到去中心化应用的基架文件:

npx create-react-app client

要在去中心化应用中使用 Infura,您需注册并设置账户,然后创建项目

在 client/src/App.js 文件中,查找 React 项目以及在 App.js 中编写的占位符代码。将占位符代码替换为以下内容:

import React from 'react';
import './App.css';
import { useWeb3 } from '@openzeppelin/network/react';
const infuraProjectId = '<YOUR_INFURA_PROJECT_ID';
function App() {
const web3Context = useWeb3(`wss://mainnet.infura.io/ws/v3/${infuraProjectId}`);
const { networkId, networkName, providerName } = web3Context;
return (
<div className="App">
	<div>
	<h1>Infura/MetaMask/OpenZeppelin Dapp</h1>
		<div>
    	Network: {networkId ? `${networkId} – ${networkName}` : 'No connection'}
		</div>
		<div>
		Provider: {providerName}
		</div>
	</div>
</div>
);
}
export default App;

现在,我们的去中心化应用将显示其当前接入的以太坊网络(主网或测试网)及其使用的 web3 提供商。

我们来测试一下:通过从 /client 目录中运行 npm start 来保存并启动浏览器。通过将 Infura websocket URL 中的 mainnet 替换为 rinkeby 进行测试。如果已经安装了 MetaMask,则禁用这里的扩展,因为您看到它具有优先性。

具体运作方式是:在上面的代码中,我们从 React 实现的 Network JS (@openzeppelin/network/react) 中导入 useWeb3,以获取 web3Context。这是一个 Javascript 钩子,它将尝试提取注入的 web3 提供商,默认为 MetaMask。如果未注入提供商,它将使用 Infura URL 集作为 web3Context。注入指的是来自用户浏览器,可供网站使用的代码或数据。

请注意,以太坊 web3 库有许多种类和语言,尽管具体实现方法有所差异,但它们都要通过“提供商”或“签名商”创建 web3 连接,因此理解这一步非常重要。

useWeb3 钩子先尝试获取注入的 web3 提供商,然后退回到网络连接。或者针对注入的 web3 提供商使用 useWeb3Injected 或针对网络提供商(例如 Infura 或私有节点)使用 useWeb3Network。

添加 React 组件

我们的下一步目标是将当前以太坊网络显示移到组件,看看在进行更改时(例如网络),会如何重新渲染组件。

为此,第一步是要选择要注入的 web3 提供商并安装它。在本教程中,我们将使用 MetaMask 作为 web3 提供商,因此我们前往 metamask.io 进行安装。

在下面的代码中,Web3Data 组件预计会得到 web3Context。从 web3Context 获取 networkId、networkName 和 providerName 并显示在组件中。

然后在 client/src 目录中创建组件目录,再使用下面的代码创建一个 Web3Data.js 文件:

import React from 'react';
export default function Web3Data(props) {
const { web3Context } = props;
const { networkId, networkName, providerName } = web3Context;
return (
<div>
	<h3> {props.title} </h3>
	<div>
    Network: {networkId ? `${networkId} – ${networkName}` : 'No connection'
	</div>
	<div>
	Provider: {providerName}
	</div>
</div>
);
}

现在回到 App.js 中,我们将添加 Web3Data 组件,以提供 web3Context。在 client/src/App.js 文件中,为 Web3Data 组件添加该导入:

import Web3Data from './components/Web3Data.js';

然后将 App.js 中的 App 函数的全部内容替换为下面的代码:

const web3Context = useWeb3(`wss://mainnet.infura.io/ws/v3/${infuraProjectId}`);
return (
<div className="App">
	<div>
	<h1>Infura React Dapp with Components!</h1>
	<Web3Data title="Web3 Data" web3Context={web3Context} />
	</div>
</div>
);

现在从客户端目录中运行 npm start,以再次启动去中心化应用,看看对账户、连接类型或网络进行更改时,Network JS 如何重新渲染 React 组件。您可以通过更改 MetaMask 中的网络来进行测试。

请求访问账户地址

请求并显示用户的账户地址是去中心化应用最常见的模式之一。但是,只有当用户被授予权限后,注入的 web3 提供商才会向去中心化应用提供对用户地址的访问权限。该约束是故意设计的,目的是保护用户隐私,防止有人恶意使用账户地址在网络上跟踪用户。在这里可以获取关于该通信协议的更多信息。

在我们的 Web3Data.js 文件中,我们将该组件内容替换为下面的代码:

import React, { useCallback } from 'react';
export default function Web3Data(props) {
const { web3Context } = props;
const { networkId, networkName, accounts, providerName } = web3Context;
const requestAuth = async web3Context => {
try {
	await web3Context.requestAuth();
	} catch (e) {
	console.error(e);
	}
};
    
const requestAccess = useCallback(() => requestAuth(web3Context), []);

return (
<div>
<h3> {props.title} </h3>
	<div>
    Network: {networkId ? `${networkId} – ${networkName}` : 'No connection'}
	</div>
	<div>
	Your address: {accounts && accounts.length ? accounts[0] : 'Unknown'}
	</div>
	<div>
	Provider: {providerName}
	</div>

	{accounts && accounts.length ? (
	<div>
	Accounts & Signing Status: Access Granted
	</div>
	) : !!networkId && providerName !== 'infura' ? (
	<div>
	<button onClick={requestAccess}>Request Access</button>
	</div>
	) : (
	<div></div>
	)}
</div>
);
}

上面的代码所执行的操作如下:

  • 从 web3Context 拉取账户,并在去中心化应用中显示可用的用户地址;
  • 如果我们请求的账户不可用,去中心化应用将生成一个按钮。用户可以通过该按钮向去中心化应用提供对其地址的访问权限;
  • 用户按下该按钮后,将调用 web3Context 中的 requestAuth 函数,且注入的 web3 提供商可以显示一个对话框,供用户请求获取访问权限。使用 React 功能时,将使用 useCallback 设置“账户访问请求”回调函数。

我们现在通过从客户端目录中运行 npm start 来再次启动该去中心化应用。

在浏览器中,按下“Request Access”以请求访问用户地址,然后接受 Metamask 弹出窗口(如果没有弹出窗口,则为扩展)中的请求。这时将显示用户地址。要重新开始该流程,退出 Metamask 即可再次提交用户访问请求。

账户余额

最后,我们的第三个目标是添加 React 组件以显示账户余额。

在下面的代码中,我们获取 lib 对象,其中包含我们需要的区块链。该对象来自 web3Context,这是 web3.js(Network.js 所依赖的代码库)的一个初始化实例。

我们使用适用于方法 getBalance 的 lib 来请求获取账户余额,它将返回以单位 wei 显示的余额值。使用“以太”单位更加方便,因此我们使用 fromWei 方法进行转换。

我们希望跟踪账户余额的状态,因此使用特定于 React 的函数,名为 useState。我们还使用 useEffect 来跟踪账户余额或 networkId 是否发生变化,如果有变化,则进行相应的更新。

为了使去中心化应用显示账户余额,我们将 Web3Data.js 文件中的组件内容替换为下面的代码:

import React, { useState, useEffect, useCallback } from 'react';
export default function Web3Data(props) {
const { web3Context } = props;
const { networkId, networkName, accounts, providerName, lib } = web3Context;
const [balance, setBalance] = useState(0);
const getBalance = useCallback(async () => {
	let balance = accounts && accounts.length > 0 ? lib.utils.fromWei(await lib.eth.getBalance(accounts[0]), 'ether') : 'Unknown';
	setBalance(balance);
}, [accounts, lib.eth, lib.utils]);
    
useEffect(() => {
getBalance();
}, [accounts, getBalance, networkId]);

const requestAuth = async web3Context => {
	try {
		await web3Context.requestAuth();
	} catch (e) {
		console.error(e);
	}
};
const requestAccess = useCallback((web3Context) => requestAuth(web3Context), []);
return (
<div>
<h3> {props.title} </h3>
	<div>
    Network: {networkId ? `${networkId} – ${networkName}` : 'No connection'}
    </div>
	<div>
    Your address: {accounts && accounts.length ? accounts[0] : 'Unknown'}
    </div>
	<div>
    Your ETH balance: {balance}
    </div>
	<div>
    Provider: {providerName}
    </div>
	{accounts && accounts.length ? (
	<div>
    Accounts & Signing Status: Access Granted
    </div>
	) : !!networkId && providerName !== 'infura' ? (
	<div>
	<button onClick={requestAccess}>Request Access</button>
	</div>
	) : (
	<div></div>
	)}
</div>
);
}

我们通过运行客户端目录中的 npm start 再次启动去中心化应用。好了!去中心化应用现在应该会显示账户余额。

您刚刚构建了去中心化应用前端(使用 Network.js)。它可以接入以太坊主网并向主网请求数据(使用 Infura)。用户(使用 Metamask)“登录”后,去中心化应用显示用户需要的信息并响应状态变更。

持续学习

我们的下一期教程将向您介绍如何显示用户的 ERC20 余额并支持用户转移余额。更多 web3 开发指南将包括众多主题,例如使用 Ethers.js 进行构建、使用 OpenZeppelin 的新手入门套件,以及更多复杂集成,例如使用 Truffle 的 Drizzle 框架设置以太坊日志筛选器,从而在前端实时响应区块链数据变更。


不错过任何一个教程 - 订阅我们的新闻资讯以及时获取最新信息。准备试用我们的 Infura 了吗?注册即可开始免费构建!