前回のサンプルアプリ作成ではReactとスマートコントラクトの連携をさせて匿名掲示板のサンプルアプリを作成しました。
今回は加えてReduxという技術を利用してみました。
状態管理する専用の場所(ストア)で状態管理するようにすること。それによってReact側ではストアの内容を反映することに専念できるように。
実装するにあたってTruffle BoxesでReduxを用いたreact-box-web3-todo というプロジェクトがありましたのでそれを元に実装しています。
他にもいくつかReduxを用いているプロジェクトがありましたのでコードを追ってみたのですが上記のプロジェクトが一番基本としては良さそうでした。
Contents
サンプルアプリのコードから得られる知識
- ERC721の利用方法
- React.jsの基本
- Reduxの基本
- フロントエンドからのスマートコントラクトとの連携方法
- Bulmaの利用方法
掲示板の仕様
匿名掲示板への投稿と一覧表示のみできるシンプルな作りとなっています。
前回の記事と同じことが実現できるようにしています。
- 掲示板への投稿
- 掲示板の一覧表示
動作環境
動作させた環境は以下になります。
- React.js v15.4.2
- Bulma v0.7.1
- yarn v1.6.0
- solidity v0.4.23
- redux v3.7.2
- OpenZeppelin v1.9.0
- truffle v4.1.7
- Ganache v1.1.0
サンプルアプリの動作確認
動作方法
リポジトリをクローン
1 |
$ git clone git@github.com:Matsushin/dapps-react-redux-bbs.git |
各ライブラリのインストール
1 |
$ yarn install |
コントラクトコードのコンパイルを実行
1 |
$ truffle compile |
Ganache CLIを利用。ブロックチェーンをローカル環境に構築する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
$ ganache-cli -b 3 Ganache CLI v6.1.0 (ganache-core: 2.1.0) Available Accounts ================== (0) 0x0a29e95d2deeccd037fce71a0046ddb21697d129 (1) 0xff1e712b8770c5d4c6d3a6c8d48193fa0dff71da (2) 0xbabdd96ea125d69892605235c2955a6b618b6670 (3) 0x42d80337b1dc7228a6ba90ad8ec7981dcb06ce62 (4) 0xa5dc85807cdb11fe039a408dc63550dbf4d19372 (5) 0x1e2f30e47589957bd87f94562e6569860f47b6d3 (6) 0x169e9286c770742db6575e123275ab53c171fb57 (7) 0x55a772d4ae0d89ec5c61ce30ec678cb1a8b7e1cf (8) 0x3dd9953245333e875cf04fbf5ae32bfd2c04ec71 (9) 0x967e6ebe4dda482cb7d621220ea407b0dfd84cf2 Private Keys ================== (0) 0c0855e20342ea59bdddb756749751ba51ba79af8d0b3d36a62aa2fcafc2ef73 (1) c6b1a374958ed9e0a0a192a707ec9e6f0c5558188eca988159c6572b3850e087 (2) debcccf56e96eb2271b9407c98409260f7733082286f02a811ca46bf5492cbc9 (3) d805aa5308c1f3ddadf3ad1ac8bd90675b830027b7f4bf8dc826333a1fa096f4 (4) 27041cae67616e7606d9fe5db0053f755bdfa6ff5326bbca313e4a898ad22ce6 (5) ee5980735d2397b0f8c42350e538d21a3059310961d3c7d2579394e1297d62dd (6) 4d742534aa60ea27d46c23147be1fbac3a810941c957d4a40afaa23467cc4edf (7) 8d082c4820fc7011ef7a8d518e5893a54ea584df03cfc1278e60e9e30a516eb1 (8) 3ebcb0020161e541effb79f7c0a698e33e8a3aa20c511d56c58900ccee8e75d7 (9) 6a1069a675bb4f8c1c468cc6d3fccb07d0a51b48e0e7f50315d8e29f9e1dff25 |
コントラクトコードのマイグレートを実行
1 |
$ truffle migrate |
ローカルサーバを起動
1 |
$ yarn start |
トップ画面(http://localhost:3000)にアクセス。エラーが発生していないことを確認。
最後にGanache CLIで生成したプライベートネットワークのアカウントをMetaMaskで使えるようにします。
メニューを開いて「Custom RPC」を選択。
RPCに「http://localhost:8545」を入力。ついでにCurrent ConversionでJPYを選択。
プライベートネットワークに接続できるようになったので次はアカウントをインポート。
ganache-cliコマンドで発行された秘密鍵(Private Keys)のどれか1つを入力。
IMPORTしたアカウントのETHの残高が表示されていればOKです!
それでは次に動作を確認していきましょう。
動作確認
※前回の記事と違うのは今回は投稿時にMetaMaskを使うようにしています。
トップ画面にアクセスして投稿フォームにタイトルと本文を入力して投稿する
MetaMaskからトランザクション発行のポップアップが表示されるのでSUBMITする
投稿後に投稿一覧に表示され、リロードしても表示されていればOK!
動作が確認できましたので次は実装のポイントを見ていきます。
今回のサンプルではトランザクション発行時にSUBMITではなくREJECTでも投稿一覧に追加されてしまっています。
サンプルコードがReduxを活かしきれていないため、こうなっています。
実装のポイント
Redux連携
Actionの定義です。現在の状態(ストア)に対して行うイベントの定義を行っています。
src/actions/index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
import Web3 from 'web3'; import contract from 'truffle-contract'; import PostTokenContract from '../..//build/contracts/PostToken.json'; export const WEB3_CONNECTED = 'WEB3_CONNECTED'; export const WEB3_DISCONNECTED = 'WEB3_DISCONNECTED'; export const POST_CONTRACT_INSTANTIATED = 'POST_CONTRACT_INSTANTIATED'; export const POSTS_FETCHED = 'POSTS_FETCHED'; export const POST_FETCHED = 'POST_FETCHED'; export const POST_ADDED = 'POST_ADDED'; export const defaultState = { web3: null, postIds: [] }; export function web3connect() { return (dispatch) => { const web3 = window.web3; // Checking if Web3 has been injected by the browser (Mist/MetaMask) if (typeof web3 !== 'undefined') { // Use Mist/MetaMask's provider. dispatch({ type: WEB3_CONNECTED, payload: new Web3(web3.currentProvider) }); } else { dispatch({ type: WEB3_CONNECTED, payload: new Web3(new Web3.providers.HttpProvider('http://localhost:8545')) }); } }; } export function instantiatePostContract() { return (dispatch, getState) => { const web3 = getState().web3; const post = contract(PostTokenContract); post.setProvider(web3.currentProvider); return post.deployed().then((postContract) => { dispatch({ type: POST_CONTRACT_INSTANTIATED, payload: postContract }); }); }; } ... |
Reducerの定義。Actionの結果を現在の状態(ストア)に反映させます。
src/reducers/index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
import { WEB3_CONNECTED, POST_ADDED, POST_CONTRACT_INSTANTIATED, POSTS_FETCHED, POST_FETCHED, defaultState } from '../actions'; const post = (state = defaultState, action) => { switch (action.type) { case WEB3_CONNECTED: return { ...state, web3: action.payload }; case POST_CONTRACT_INSTANTIATED: return { ...state, postContract: action.payload }; case POSTS_FETCHED: return { ...state, postIds: action.payload }; case POST_FETCHED: return { ...state, post: action.payload }; case POST_ADDED: return { ...state, postIds: [ ...state.postIds, action.payload ] }; default: return state } }; export default post; |
まとめ
今回サンプルアプリを作ってみましたが、あまりReduxを活かしきれていない感じがしています 。
そのため、Reactでの処理が増えてしまっています。具体的には投稿ID一覧を取得した後1つずつ投稿詳細データを取得する箇所ですね。
この辺りもう少し学習をしていきたいと思います。うまくやればもっと状態管理とコンポーネント内の処理を分離させることができそうです。
書籍も購入したのでもう少し基礎から学習してみることにしました。
React入門 React・Reduxの導入からサーバサイドレンダリングによるUXの向上まで
参考にした記事
たぶんこれが一番分かりやすいと思います React + Reduxのフロー図解
はじめまして、こんにちは
のっち と申します
自分は、最近スマートコントラクトを勉強し始めたばかりの、プログラミング初心者です
最近、ブロックチェーンという言葉をよく耳にするし、
今後、勉強していても損にはならないだろうという漠然とした動機から
オンラインスクールで勉強しているのですが、プログラミング初心者の自分には、
わからないことが多いです
課題で掲示板用のコントラクトの作成をするのですが、
どうやって組み立てていったらいいのか、何か参考になるものがないかと思い、検索していたところ、
まつしんさんのブログにたどりつきました
キャリアが全然違いますが、いろいろと参考にさせていただきたいと思います
どうぞよろしくお願いします
初めまして、まつしんです。のっちさんコメントありがとうございます!
そうですねこれからDApps開発ができるプログラマの需要は間違いなく増えると見ています。
まだできる人もそんなに多くないと思いますしね。
頑張っていきましょうー!