0から作るDapps入門 フロントエンド編
はじめに
みなさん、こんにちは株式会社Acompanyの湯浅です. この記事は0から作るDapps入門のPart2です. Part1はこちらからお読みください.
今回はreactを用いたフロントエンド開発を行います.
今更ですが、このアプリケーションの構成図はこのようになっています.
シンプルですね. 今回はReactというフロントエンドフレームワークを使ってweb3.jsを経由してmetamaskを動かし, スマートコントラクトを動作させます.
セットアップ
まずディレクトリを作ります.
そこにweb3がすぐに使えるようになっているReact Truffle Boxというレポジトリをクローンします. npm install
を実行することで package.json
書かれている dependencies
をインストールしてくれます.
$ git clone https://github.com/truffle-box/react-box.git
$ cd react-box/client
$ npm install
Part1で作った dapps-sample
ディレクトリ下の build/contract
に Recorder.json
ファイルが生成されています. これはコントラクトのコンパイル時に生成されたものです. 中身を丸ごとコピーしましょう.
そして react-box
ディレクトリのclient/src
下に Recorder.json
というファイルを作ってそこに貼ります.
作っていく
client/src
下の App.js
をとりあえずこのように書き換えましょう.
import React, { Component } from "react";
import Recorder from "./Recorder.json";
import getWeb3 from "./utils/getWeb3";
import "./App.css";class App extends Component {
state = { web3: null, accounts: null, contract: null, weight: null, height: null, address: "", outputWeight: null, outputHeight: null, time: null}; componentDidMount = async () => {
try {
const web3 = await getWeb3();
const accounts = await web3.eth.getAccounts(); const networkId = await web3.eth.net.getId(); const deployedNetwork = Recorder.networks[networkId];
const instance = new web3.eth.Contract(
Recorder.abi,
deployedNetwork && deployedNetwork.address,
);
this.setState({ web3, accounts, contract: instance });
} catch (error) {
alert(
`Failed to load web3, accounts, or contract. Check console for details.`,
);
console.error(error);
}
};render() {
return (
<div className="App">
</div>
);
}
}export default App;
componentDidMount
は react
のライフサイクルの一種でレンダリングがされた直後に実行される関数です. web3とコントラクトを使えるようにする処理を書いています.
render
はHTML的なものを書いていく所です.
とりあえず起動してみましょう.
$ npm start
何も表示されませんね.
関数の作成
では先に関数を作りましょう.
writeRecord = async() => {
const { accounts, contract, weight, height } = this.state;
const result = await contract.methods.writeData(parseInt(weight), parseInt(height)).send({
from: accounts[0]
});
console.log(result);
if(result.status === true) {
alert('記録が完了しました');
}
}
これはコントラクトで定義した writeData
関数を実行する関数です. ユーザーの weight
と height
という入力値を受け取って、引数に入れています. writeData
はコントラクトのStorageにデータを書き込む関数なのでGas代を支払うアカウントを明記しています. ここでは自分のデータを書き込むユーザーが支払うようになっています.
viewRecord = async() => {
const { contract, address } = this.state;
const result = await contract.methods.viewData(address).call();
console.log(result);
const outputWeight = result[0];
const outputHeight = result[1];
const time = this.unixTimeToTime(result[2]);
this.setState({ outputWeight, outputHeight, time });
}
これも同様にコントラクトで定義した viewData
関数を実行する関数です.
writeRecord
関数と違うところはGas代を支払うユーザーを明記していない点です. viewData
関数はStorageにデータを書き込まない(見るだけ)なのでGas代を支払う必要がありません. また受け取った結果を表示するためにstateに入れています.
unixTimeToTime = (intTime) => {
const time = Number(intTime);
const y = new Date(time * 1000);
const year = y.getFullYear();
const month = y.getMonth() + 1;
const day = y.getDate();
const hour = ('0' + y.getHours()).slice(-2);
const min = ('0' + y.getMinutes()).slice(-2);
const sec = ('0' + y.getSeconds()).slice(-2);
const Time = year + '-' + month + '-' + day + ' ' + hour + ':' + min + ':' + sec;
return Time;
}
こちらはunixtimeを通常の時間に変換する関数です. コントラクトで記録した時間はunixtimeなので通常の時間に変更して表示させます.
handleChange = name => event => {
this.setState({ [name]: event.target.value });
}
最後はユーザーの入力値を受け取ってstateに入れる関数です.
UIの作成
では次にUIを作っていきましょう.
render() {
return(
<div className="App">
<input onChange={this.handleChange('weight')} />
<input onChange={this.handleChange('height')} />
<button onClick={this.writeRecord}>記録</button>
<br/>
<br/>
<input onChange={this.handleChange('address')} />
<button onClick={this.viewRecord}>閲覧</button>
<br/>
<br/>
{this.state.outputWeight? <p>体重: {this.state.outputWeight}</p>: <p></p>}{this.state.outputHeight? <p>身長: {this.state.outputHeight}</p>: <p></p>}
{this.state.time? <p>時間: {this.state.time}</p>: <p></p>}
</div>
);
}
かなりシンプルなUIが完成しました.
テスト
では動かしてみましょう. 体重と身長を入力して「記録」をクリックします.
メタマスク が起動するのでOKを押します.
トランザクションの送信が完了するとこのようになります.
次に閲覧をしてみましょう. メタマスクを開いてアドレスをコピーします.
それを入力して閲覧をクリックします.
閲覧もできましたね.
App.js
の最終状態をみたい方は以下からご覧ください.
まとめ
今回はかなりシンプルな構成でしたが, コントラクトの作成, デプロイ, それをフロントから呼び出す方法はわかったと思います. なのでこれらを拡張することで作りたいDappsを作れるはずです.
今後も,Acompanyからブロックチェーンに関する記事を投稿していきますので,ぜひfollowしていただけると嬉しいです.
Happy Hacking 😎!