drizzle 和 react 学习
是 Suite 的最新成员,也是我们的第一个前端开发工具。 的核心是将合约数据和交易数据等内容从区块链同步至 Redux store。 基础库顶部有更高级别的抽象; 用于React兼容性 (-react) 和一组即用型 React 组件 (-react-) 的工具。
今天我们专注于初级应用,带你从头开始用 React 和 设置 项目。通过这种方式,我们可以更好地了解 在应用中如何使用。有了这些知识,您可以利用您选择的任何前端框架充分利用 ,或者放心使用更高级别的React抽象
这将是一个非常初级的教程,专注于设置和获取存储在合约中的简单字符串。它适用于具有 基础知识的人,此外还需要对 和 React.js 有一定的了解,但是不太熟悉 。
注意:了解 基础,可以参考 Pet Shop 教程
本教程将覆盖:
设置开发环境
从头开始创建 项目
编写智能合约
编译和移植智能合约
测试智能合约
创建 React.js 项目
设置前端客户端
用 连接 React 应用程序
写一个从 中读取的组件
写一个写入智能合约的组件
1. 设置开发环境
开始之前有一些技术要求。请安装以下内容:
1.1
安装如上内容后,安装 :
npm install -g truffle
验证 是否已正确安装,在终端上输入 。如果发现错误,请检查是否已将npm模块添加到路径中。
1.2 -CLI
我们还将使用 -CLI ,这是一个用于以太坊开发的个人区块链,可用于部署合约、开发应用程序和运行测试。 可以使用以下命令全局安装:
npm install -g ganache-cli
这里如果出现非全局安装问题,可使用 ln -s 将 -cli 链接至 /usr/local/bin/ 。详细使用方法请
1.3 -React-App
最后,由于这是一个 React.js 教程,我们将使用 -React-App 创建我们的React项目
如果你拥有 NPM 5.2 或更高版本,则无需执行任何操作。你可以通过运行 npm -- 来检查 NPM 版本。如果没有,则需要使用以下命令全局安装该工具:
npm install -g create-react-app
2. 创建 项目
在当前目录中初始化,因此首先在你选择的开发文件夹中创建一个目录,然后移动至目录
mkdir drizzle-react-tutorial
cd drizzle-react-tutorial
运行如下命令生成空 项目
truffle init
简单看一下生成的目录结构
2.1 目录结构
默认的目录结构包含如下:
3. 编写智能合约
我们将添加一个名为 的简单智能合约。
在 / 目录中创建名为 .sol 的新文件。
文件中添加如下内容:
pragma solidity ^0.5.0;contract MyStringStore {
string public myString = "Hello World";function set(string memory x) public {myString = x;
}
}
由于这不是教程,所以您需要了解的是:
注意:
这里需要在 set 函数传入参数 x 中添加 属性,不然可能会在 4.1 编译部分出现如下错误(应该是 版本的问题):
3.1 运行测试链
在我们继续操作之前,让我们首先使用 -CLI 启动我们的测试区块链。
打开一个新终端并运行以下命令:
ganache-cli -b 3
这将生成一个新的区块链,默认情况下会侦听127.0.0.1:8545,并将每3秒进行一次挖矿。 如果我们没有指定这个,会立即挖矿,我们将无法模拟真正的区块链开采所需的延迟。
保持终端窗口打开,您可以观察稍后与之交互时发生的情况
3.2 指定网络
我们需要让我们的 项目知道如何连接到这个区块链。 要做到这一点,我们需要将以下内容放在 -.js中 :
module.exports = {networks: {development: {host: "localhost",port: 8545,network_id: "*" // Match any network id}}
};
4. 编译和移植智能合约
现在我们准备编译和移植合约
4.1 编译
在终端中,确保您位于项目目录的根目录中并输入:
truffle compile
如果您使用的是 并且在运行此命令时遇到问题,请参阅文档 on
您应该看到类似于以下输出内容:
Compiling ./contracts/Migrations.sol...
Compiling ./contracts/MyStringStore.sol...
Writing artifacts to ./build/contracts
4.2 移植
现在我们已经成功编译了合约,是时候将它们移植到区块链了!
注意:关于移植
创建移植脚本
在 / 目录中创建名为 .js 的新文件
文件中添加如下内容:
const MyStringStore = artifacts.require("MyStringStore");module.exports = function(deployer) {
deployer.deploy(MyStringStore);
};
返回终端,移植合约到区块链
truffle migrate
你可以看到如下类似输出:
Using network 'development'.Running migration: 1_initial_migration.jsDeploying Migrations...... 0xcc1a5aea7c0a8257ba3ae366b83af2d257d73a5772e84393b0576065bf24aedfMigrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0
Saving successful migration to network...... 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956
Saving artifacts...
Running migration: 2_deploy_contracts.jsDeploying MyStringStore...... 0x43b6a6888c90c38568d4f9ea494b9e2a22f55e506a8197938fb1bb6e5eaa5d34MyStringStore: 0x345ca3e014aaf5dca488057592ee47305d9b3e10
Saving successful migration to network...... 0xf36163615f41ef7ed8f4a8f192149a0bf633fe1a2398ce001bf44c43dc7bdda0
Saving artifacts...
您可以按顺序查看正在执行的移植,然后是每个已部署合同的区块链地址(您的地址将有所不同)。
5. 测试智能合约
在我们继续之前,我们应该编写几个测试来确保合约按预期工作。
在 test/ 目录中创建一个名为 .js 的新文件。
将以下内容添加到 .js 文件:
const MyStringStore = artifacts.require("./MyStringStore.sol");contract("MyStringStore", accounts => {
it("should store the string 'Hey there!'", async () => {const myStringStore = await MyStringStore.deployed();// Set myString to "Hey there!"await myStringStore.set("Hey there!", { from: accounts[0] });// Get myString from public variable getterconst storedString = await myStringStore.myString.call();assert.equal(storedString, "Hey there!", "The string was not stored");
});
});
5.1 运行测试
返回终端,运行测试:
truffle test
如果所有测试都通过,您将在控制台看到与此类似的输出:
Using network 'development'.Contract: MyStringStore✓ should store the value 'Hey there!' (3085ms)1 passing (3s)
真棒! 现在我们知道合同确实有效。
6. 创建 React.js 项目
现在我们完成了智能合约,我们可以用 React.js 编写我们的前端客户端! 为此,只需运行此命令(如果您有NPM 5.2或更高版本):
npx create-react-app client
如果您有较旧版本的 NPM ,请确保按照 up the 部分中的说明全局安装 -React-App ,然后运行以下命令:
create-react-app client
这应该在您的 项目中创建一个 目录,并生成一个裸的 React.js 项目,让你开始构建您的前端。
7. 设置前端客户端
现在我们在 目录中有一个前端客户端,使用命令 cd 切换到该目录,然后继续执行以下步骤进行设置。
7.1 链接编译文件
由于 -React-App 默认不允许从 src 文件夹外部导入文件,因此我们需要将我们 build 文件夹中的 放入 src 中。 我们可以在每次编译合约时复制和粘贴一次,但更好的方法是创建符号链接。
如果你之前没有创建过符号链接,请将其视为文件系统中的一个
切换到 src 目录,然后创建符号链接文件夹:
// For MacOS and Linuxcd src
ln -s ../../build/contracts contracts// For Windows 7, 8 and 10
// Using a Command Prompt as Admincd src
mklink /D contracts ..\..\build\contracts
实际上,这应该在 src 中创建看起来像 文件夹的内容,但它实际上指向我们的 项目的 build/ 文件夹中的文件。
7.2 安装
这是最有趣的部分,我们安装 。 使用命令 cd .. 更改回 目录,然后运行以下命令:
npm install drizzle
这就是依赖关系! 请注意,我们不需要自己安装 Web3.js 或 - 。 包含我们与智能合约交互所需的一切。
8. 用 连接 React 应用程序
在我们进一步讨论之前,让我们通过在 目录中运行如下命令来启动我们的 React 应用程序:
npm start
这将在 :3000 下启动前端服务,因此请在浏览器中打开它
注意:如果已经安装了 ,请确保使用隐身窗口(或暂时禁用 )。 否则,应用程序将尝试使用 中指定的网络,而不是 :8545 下的开发网络。
如果加载的默认 -React-App 页面没有任何问题,你可以继续。
8.1 设置商店
我们需要做的第一件事是设置和实例化商店。我们把以下5行添加到 /src/index.js :
// import drizzle functions and contract artifact
import { Drizzle, generateStore } from "drizzle";
import MyStringStore from "./contracts/MyStringStore.json";// let drizzle know what contracts we want
const options = { contracts: [MyStringStore] };// setup the drizzle store and drizzle
const drizzleStore = generateStore(options);
const drizzle = new Drizzle(options, drizzleStore);
首先,我们从 和软连接的合约定义中导入工具
然后我们为 构建了我们的选项对象,在这种情况下,它只是通过传入 JSON 构建工件来指定我们想要加载的特定合约。
最后,我们创建了 并使用它来创建我们的 实例,我们将其作为支持传递给我们的 App 组件。
完成后,index.js 应如下所示:
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import registerServiceWorker from "./registerServiceWorker";// import drizzle functions and contract artifact
import { Drizzle, generateStore } from "drizzle";
import MyStringStore from "./contracts/MyStringStore.json";// let drizzle know what contracts we want
const options = { contracts: [MyStringStore] };// setup the drizzle store and drizzle
const drizzleStore = generateStore(options);
const drizzle = new Drizzle(options, drizzleStore);// pass in the drizzle instance
ReactDOM.render(<App drizzle={drizzle} />, document.getElementById("root"));
registerServiceWorker();
再次注意, 实例作为 props 传递到 App 组件。
8.2 连接 App 组件
现在我们有一个 实例,我们可以进入 /src/App.js 开始使用 React API。
8.2.1 添加状态变量
我们要做的第一件事是在我们的 App 组件中添加以下行
state = { loading: true, drizzleState: null };
我们将在这里使用两个状态变量:
— 指示 是否已完成初始化并且应用程序已准备就绪。 初始化过程包括实例化 web3 和我们的智能合约,获取任何可用的以太坊帐户并监听(或者,在不支持订阅的情况下:轮询)新块。
— 这是我们将 商店的状态存储在顶级组件中的位置。 如果我们可以保持这个状态变量是最新的,那么我们可以简单地使用简单的 props 和 state 来使用 (即你不必使用任何 Redux 或高级 React 模式)。
8.2.2 添加一些初始化逻辑
接下来,我们将 方法添加到组件类中,以便我们可以运行一些初始化逻辑。
componentDidMount() {const { drizzle } = this.props;// subscribe to changes in the storethis.unsubscribe = drizzle.store.subscribe(() => {// every time the store updates, grab the state from drizzleconst drizzleState = drizzle.store.getState();// check to see if it's ready, if so, update local component stateif (drizzleState.drizzleStatus.initialized) {this.setState({ loading: false, drizzleState });}});
}
首先,我们从 props 中获取 实例,然后我们调用.store. 并传入一个回调函数。 每当更新 存储时,都会调用此回调函数。 请注意,这个商店实际上是一个 Redux 商店,所以如果您之前使用过 Redux,这可能看起来很熟悉。
每当更新商店时,我们将尝试使用 .store.() 获取状态,然后如果初始化并准备好 ,我们将 设置为false,并更新 状态变量。
通过这样做, 将始终是最新的,我们也确切知道 何时准备就绪,因此我们可以使用加载组件让用户知道。