Mở đầu
Đầu tiên chúng ta sẽ xây dựng một ứng dụng với phần smart contract đã được xây dựng ở bài trên
Phía frontend sẽ sử dụng create-react-app để init project:
1 2 | npx create<span class="token operator">-</span>react<span class="token operator">-</span>app react<span class="token operator">-</span>frontend |
Nếu chưa có gói create-react-app thì hãy install bằng lệnh sau:
1 2 | <span class="token function">npm</span> <span class="token function">install</span> -g create-react-app |
Sau khi đã Happy Hacking! thì chúng ta sẽ config lại một chút các package, mặc dù quy mô của app demo này không cần thiết phải dùng đến redux vì không chứa quá nhiều components tuy nhiên để hướng dẫn cho các app phức tạp sau này, mình sẽ tạm ứng dụng thêm redux để quản lý các state.
=> Do đó yêu cầu người đọc cần đã có kiến thức cơ bản về redux
Thêm các dependencies :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token string">"dependencies"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token string">"@testing-library/jest-dom"</span><span class="token punctuation">:</span> <span class="token string">"^4.2.4"</span><span class="token punctuation">,</span> <span class="token string">"@testing-library/react"</span><span class="token punctuation">:</span> <span class="token string">"^9.3.2"</span><span class="token punctuation">,</span> <span class="token string">"@testing-library/user-event"</span><span class="token punctuation">:</span> <span class="token string">"^7.1.2"</span><span class="token punctuation">,</span> <span class="token string">"react"</span><span class="token punctuation">:</span> <span class="token string">"^16.12.0"</span><span class="token punctuation">,</span> <span class="token string">"react-dom"</span><span class="token punctuation">:</span> <span class="token string">"^16.12.0"</span><span class="token punctuation">,</span> <span class="token string">"react-scripts"</span><span class="token punctuation">:</span> <span class="token string">"3.3.0"</span><span class="token punctuation">,</span> <span class="token string">"react-redux"</span><span class="token punctuation">:</span> <span class="token string">"^7.0.3"</span><span class="token punctuation">,</span> <span class="token string">"redux"</span><span class="token punctuation">:</span> <span class="token string">"^4.0.1"</span><span class="token punctuation">,</span> <span class="token string">"redux-thunk"</span><span class="token punctuation">:</span> <span class="token string">"^2.3.0"</span><span class="token punctuation">,</span> <span class="token string">"thunk"</span><span class="token punctuation">:</span> <span class="token string">"^0.0.1"</span><span class="token punctuation">,</span> <span class="token string">"web3"</span><span class="token punctuation">:</span> <span class="token string">"1.0.0-beta.55"</span> <span class="token punctuation">}</span> |
Thêm 3 folder action, reducers và store vào project :
Tiếp đó là thêm cả phần contracts trong folder build của smart contract sau khi được migrate vào phần src để có thể dễ dàng tương tác hơn
Triển khai ứng dụng
Actions
Đầu tiên sẽ bắt đầu từ folder actions,
Đầu tiên sẽ là action để yêu cầu kết nối với metamask để lấy web3. Để gọi pop-up metamask thì trước hết các bạn add thêm function getWeb3() vào folder utils/getWeb3.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 | <span class="token keyword">import</span> Web3 <span class="token keyword">from</span> <span class="token string">'web3'</span><span class="token punctuation">;</span> <span class="token keyword">const</span> getWeb3 <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// Wait for loading completion to avoid race conditions with web3 injection timing.</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>window<span class="token punctuation">.</span>ethereum<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> web3 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Web3</span><span class="token punctuation">(</span>window<span class="token punctuation">.</span>ethereum<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token comment">// Request account access if needed</span> <span class="token keyword">await</span> window<span class="token punctuation">.</span>ethereum<span class="token punctuation">.</span><span class="token function">enable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Acccounts now exposed</span> <span class="token keyword">return</span> web3<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// Legacy dapp browsers...</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>window<span class="token punctuation">.</span>web3<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Use Mist/MetaMask's provider.</span> <span class="token keyword">const</span> web3 <span class="token operator">=</span> window<span class="token punctuation">.</span>web3<span class="token punctuation">;</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Injected web3 detected.'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> web3<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Fallback to localhost; use dev console port by default...</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> provider <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Web3<span class="token punctuation">.</span>providers<span class="token punctuation">.</span>HttpProvider</span><span class="token punctuation">(</span><span class="token string">'http://127.0.0.1:9545'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> web3 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Web3</span><span class="token punctuation">(</span>provider<span class="token punctuation">)</span><span class="token punctuation">;</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'No web3 instance injected, using Local web3.'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> web3<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">default</span> getWeb3<span class="token punctuation">;</span> |
Viết function đầu tiên web3Connect():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <span class="token keyword">import</span> getWeb3 <span class="token keyword">from</span> <span class="token string">'utils/getWeb3'</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token constant">WEB3_CONNECT</span> <span class="token operator">=</span> <span class="token string">'WEB3_CONNECT'</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token function-variable function">web3Connect</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">async</span> <span class="token punctuation">(</span>dispatch<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> web3 <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">getWeb3</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> accounts <span class="token operator">=</span> <span class="token keyword">await</span> web3<span class="token punctuation">.</span>eth<span class="token punctuation">.</span><span class="token function">getAccounts</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>web3<span class="token punctuation">.</span>currentProvider<span class="token punctuation">.</span>connection<span class="token punctuation">.</span>networkVersion <span class="token operator">!==</span> <span class="token string">'3'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">alert</span><span class="token punctuation">(</span><span class="token string">'Unknown network, please change network to Ropsten network'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>accounts<span class="token punctuation">.</span>length <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> account <span class="token operator">=</span> accounts<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">let</span> balance <span class="token operator">=</span> <span class="token keyword">await</span> web3<span class="token punctuation">.</span>eth<span class="token punctuation">.</span><span class="token function">getBalance</span><span class="token punctuation">(</span>account<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token punctuation">{</span> type<span class="token punctuation">:</span> <span class="token constant">WEB3_CONNECT</span><span class="token punctuation">,</span> web3<span class="token punctuation">,</span> account <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Account not found'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> |
Tiếp đó sẽ là action Init các contract:
Function này sẽ lạ hơn một chút vì sẽ cần đến file mà chúng ta đã deployed trong phần contract. Công việc của chúng ta là add them folder contract từ folder build phía trên. Như đã nói thì chỉ cần 2 thứ là ABI và Address là có thể tạo ra instance tương tác với blockchain
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <span class="token keyword">import</span> MetaCoin <span class="token keyword">from</span> <span class="token string">'contracts/MetaCoin.json'</span><span class="token punctuation">;</span> <span class="token operator">*</span> <span class="token operator">*</span> <span class="token operator">*</span> <span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token constant">INIT_CONTRACT</span> <span class="token operator">=</span> <span class="token string">'INIT_CONTRACT'</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token function-variable function">instantiateContracts</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">async</span> <span class="token punctuation">(</span>dispatch<span class="token punctuation">,</span> getState<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> state <span class="token operator">=</span> <span class="token function">getState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">let</span> web3 <span class="token operator">=</span> state<span class="token punctuation">.</span>web3<span class="token punctuation">;</span> <span class="token keyword">const</span> networkId <span class="token operator">=</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">REACT_APP_NETWORK_ID</span><span class="token punctuation">;</span> <span class="token keyword">let</span> metaCoinAddress <span class="token operator">=</span> MetaCoin<span class="token punctuation">.</span>networks<span class="token punctuation">[</span>networkId<span class="token punctuation">]</span><span class="token punctuation">.</span>address<span class="token punctuation">;</span> <span class="token keyword">let</span> metaCoin <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">web3<span class="token punctuation">.</span>eth<span class="token punctuation">.</span>Contract</span><span class="token punctuation">(</span>MetaCoin<span class="token punctuation">.</span>abi<span class="token punctuation">,</span> metaCoinAddress<span class="token punctuation">,</span> <span class="token punctuation">{</span> transactionConfirmationBlocks<span class="token punctuation">:</span> <span class="token number">1</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token punctuation">{</span> type<span class="token punctuation">:</span> <span class="token constant">INIT_CONTRACT</span><span class="token punctuation">,</span> metaCoin <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> |
Cuối cùng thêm 2 action sendCoin và getBalance:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token constant">SEND_COIN</span> <span class="token operator">=</span> <span class="token string">'SEND_COIN'</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token function-variable function">sendCoin</span> <span class="token operator">=</span> <span class="token punctuation">(</span>receiver<span class="token punctuation">,</span> amount<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span>dispatch<span class="token punctuation">,</span> getState<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> state <span class="token operator">=</span> <span class="token function">getState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> metacoin <span class="token operator">=</span> state<span class="token punctuation">.</span>metacoin<span class="token punctuation">;</span> <span class="token keyword">const</span> account <span class="token operator">=</span> state<span class="token punctuation">.</span>account<span class="token punctuation">;</span> metacoin<span class="token punctuation">.</span>methods <span class="token punctuation">.</span><span class="token function">sendCoin</span><span class="token punctuation">(</span>receiver<span class="token punctuation">,</span> amount<span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">{</span> account <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'success'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token punctuation">{</span> type<span class="token punctuation">:</span> <span class="token constant">SEND_COIN</span><span class="token punctuation">,</span> amount <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token keyword">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> |
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token constant">GET_BALANCE</span> <span class="token operator">=</span> <span class="token string">'GET_BALANCE'</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token function-variable function">getBalance</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">async</span> <span class="token punctuation">(</span>dispatch<span class="token punctuation">,</span> getState<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> state <span class="token operator">=</span> <span class="token function">getState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> metaCoin <span class="token operator">=</span> state<span class="token punctuation">.</span>metaCoin<span class="token punctuation">;</span> <span class="token keyword">const</span> account <span class="token operator">=</span> state<span class="token punctuation">.</span>account<span class="token punctuation">;</span> <span class="token keyword">let</span> balance <span class="token operator">=</span> <span class="token keyword">await</span> metaCoin<span class="token punctuation">.</span>methods<span class="token punctuation">.</span><span class="token function">getBalance</span><span class="token punctuation">(</span>account<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token keyword">from</span><span class="token punctuation">:</span> account <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token punctuation">{</span> type<span class="token punctuation">:</span> <span class="token constant">GET_BALANCE</span><span class="token punctuation">,</span> balance <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> |
Đủ các action và giờ sẽ tiếp tục xây dựng reducer
Reducer
Như các action đã được xây dựng phía trên, chúng ta sẽ xây dựng được các initialState và action type tương ứng
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 | <span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> actions <span class="token keyword">from</span> <span class="token string">'actions'</span><span class="token punctuation">;</span> <span class="token keyword">const</span> initialState <span class="token operator">=</span> <span class="token punctuation">{</span> web3<span class="token punctuation">:</span> <span class="token keyword">null</span><span class="token punctuation">,</span> account<span class="token punctuation">:</span> <span class="token keyword">null</span><span class="token punctuation">,</span> balance<span class="token punctuation">:</span> <span class="token keyword">null</span><span class="token punctuation">,</span> metaCoin<span class="token punctuation">:</span> <span class="token keyword">null</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">rootReducer</span> <span class="token operator">=</span> <span class="token punctuation">(</span>state <span class="token operator">=</span> initialState<span class="token punctuation">,</span> action<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">switch</span> <span class="token punctuation">(</span>action<span class="token punctuation">.</span>type<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">case</span> actions<span class="token punctuation">.</span><span class="token constant">WEB3_CONNECT</span><span class="token punctuation">:</span> <span class="token keyword">return</span> <span class="token punctuation">{</span> <span class="token operator">...</span>state<span class="token punctuation">,</span> web3<span class="token punctuation">:</span> action<span class="token punctuation">.</span>web3<span class="token punctuation">,</span> account<span class="token punctuation">:</span> action<span class="token punctuation">.</span>account <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">case</span> actions<span class="token punctuation">.</span><span class="token constant">INIT_CONTRACT</span><span class="token punctuation">:</span> <span class="token keyword">return</span> <span class="token punctuation">{</span> <span class="token operator">...</span>state<span class="token punctuation">,</span> metaCoin<span class="token punctuation">:</span> action<span class="token punctuation">.</span>metaCoin <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">case</span> actions<span class="token punctuation">.</span><span class="token constant">GET_BALANCE</span><span class="token punctuation">:</span> <span class="token keyword">return</span> <span class="token punctuation">{</span> <span class="token operator">...</span>state<span class="token punctuation">,</span> balance<span class="token punctuation">:</span> action<span class="token punctuation">.</span>balance <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">default</span><span class="token punctuation">:</span> <span class="token keyword">return</span> state<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">default</span> rootReducer<span class="token punctuation">;</span> |
Store
Xây dựng phần store, mình sẽ sử dụng thunk cho phần Middleware
1 2 3 4 5 6 7 | <span class="token keyword">import</span> rootReducer <span class="token keyword">from</span> <span class="token string">'reducersiddle'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> createStore<span class="token punctuation">,</span> applyMiddleware <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'redux'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> thunk <span class="token keyword">from</span> <span class="token string">'redux-thunk'</span><span class="token punctuation">;</span> <span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token function">createStore</span><span class="token punctuation">(</span>rootReducer<span class="token punctuation">,</span> <span class="token function">applyMiddleware</span><span class="token punctuation">(</span>thunk<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">default</span> store<span class="token punctuation">;</span> |
Cuối cùng để cung cấp store trong mọi component của ứng dụng, ta cần wrap chúng trong Provider
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <span class="token keyword">import</span> React <span class="token keyword">from</span> <span class="token string">'react'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> ReactDOM <span class="token keyword">from</span> <span class="token string">'react-dom'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token string">'./index.css'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> App <span class="token keyword">from</span> <span class="token string">'./App'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> serviceWorker <span class="token keyword">from</span> <span class="token string">'./serviceWorker'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> Provider <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'react-redux'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> store <span class="token keyword">from</span> <span class="token string">'store'</span><span class="token punctuation">;</span> ReactDOM<span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">(</span> <span class="token operator"><</span>Provider store<span class="token operator">=</span><span class="token punctuation">{</span>store<span class="token punctuation">}</span><span class="token operator">></span> <span class="token operator"><</span>App <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>Provider<span class="token operator">></span><span class="token punctuation">,</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'root'</span><span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// If you want your app to work offline and load faster, you can change</span> <span class="token comment">// unregister() to register() below. Note this comes with some pitfalls.</span> <span class="token comment">// Learn more about service workers: https://bit.ly/CRA-PWA</span> serviceWorker<span class="token punctuation">.</span><span class="token function">unregister</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Như vậy đã hoàn thiện phần quản lý các State, bây giờ sẽ đến phần xây dựng giao diện và ứng dụng của app
App
Với tính chất một bài cơ bản thì mình sẽ demo phần tương tác với Blockchain với 2 phương thức đơn giản:
- Tạo transaction: Thông qua hàm sendCoin đã được xây dựng ở phần actions
- Đọc thông tin: Đọc dữ liệu về balance của user thông qua hàm getBalance
Đầu tiên chúng ta sẽ bắt user đằng nhập metamask, do đó sẽ gọi function web3Connect ngay khi component được mount . Để tối ưu lượng code thì mình sẽ sử dụng 2 Hook :
- useEffect : Tương tự như tác dụng componentDidMount nhưng dùng cho function component
- useDispatch : Thay cho việc chúng ta sử dụng mapDispatchToProps
Thêm vào App.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 | <span class="token keyword">import</span> React<span class="token punctuation">,</span> <span class="token punctuation">{</span> useEffect <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'react'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> useDispatch <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'react-redux'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> logo <span class="token keyword">from</span> <span class="token string">'./logo.svg'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token string">'./App.css'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> actions <span class="token keyword">from</span> <span class="token string">'actions'</span><span class="token punctuation">;</span> <span class="token keyword">function</span> <span class="token function">App</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> dispatch <span class="token operator">=</span> <span class="token function">useDispatch</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">useEffect</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">dispatch</span><span class="token punctuation">(</span>actions<span class="token punctuation">.</span><span class="token function">web3Connect</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>div className<span class="token operator">=</span><span class="token string">'App'</span><span class="token operator">></span> <span class="token operator"><</span>header className<span class="token operator">=</span><span class="token string">'App-header'</span><span class="token operator">></span> <span class="token operator"><</span>img src<span class="token operator">=</span><span class="token punctuation">{</span>logo<span class="token punctuation">}</span> className<span class="token operator">=</span><span class="token string">'App-logo'</span> alt<span class="token operator">=</span><span class="token string">'logo'</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span>p<span class="token operator">></span> Edit <span class="token operator"><</span>code<span class="token operator">></span>src<span class="token operator">/</span>App<span class="token punctuation">.</span>js<span class="token operator"><</span><span class="token operator">/</span>code<span class="token operator">></span> and save to reload<span class="token punctuation">.</span> <span class="token operator"><</span><span class="token operator">/</span>p<span class="token operator">></span> <span class="token operator"><</span>a className<span class="token operator">=</span><span class="token string">'App-link'</span> href<span class="token operator">=</span><span class="token string">'https://reactjs.org'</span> target<span class="token operator">=</span><span class="token string">'_blank'</span> rel<span class="token operator">=</span><span class="token string">'noopener noreferrer'</span> <span class="token operator">></span> Learn React <span class="token operator"><</span><span class="token operator">/</span>a<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>header<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">default</span> App<span class="token punctuation">;</span> |
Đến đây thì khi vào trang sẽ tự động bán ra popup metamask yêu cầu đăng nhập nếu user chưa đăng nhập và sẽ tự động lấy địa chỉ account
Tiếp theo hiển thị ra balance và account, thêm một chút Hook useSelector để lấy giá trị trực tiếp từ store:
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 50 51 52 53 | <span class="token keyword">import</span> React<span class="token punctuation">,</span> <span class="token punctuation">{</span> useEffect <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'react'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> useDispatch<span class="token punctuation">,</span> useSelector <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'react-redux'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> logo <span class="token keyword">from</span> <span class="token string">'./logo.svg'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token string">'./App.css'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> actions <span class="token keyword">from</span> <span class="token string">'actions'</span><span class="token punctuation">;</span> <span class="token keyword">function</span> <span class="token function">App</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> dispatch <span class="token operator">=</span> <span class="token function">useDispatch</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> balance<span class="token punctuation">,</span> account <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useSelector</span><span class="token punctuation">(</span><span class="token punctuation">(</span>state<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span><span class="token punctuation">{</span> balance<span class="token punctuation">:</span> state<span class="token punctuation">.</span>balance<span class="token punctuation">,</span> account<span class="token punctuation">:</span> state<span class="token punctuation">.</span>account <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">useEffect</span><span class="token punctuation">(</span><span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">fetWeb3Init</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">await</span> <span class="token function">dispatch</span><span class="token punctuation">(</span>actions<span class="token punctuation">.</span><span class="token function">web3Connect</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">await</span> <span class="token function">dispatch</span><span class="token punctuation">(</span>actions<span class="token punctuation">.</span><span class="token function">instantiateContracts</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">function</span> <span class="token function">getBalance</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">setInterval</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">dispatch</span><span class="token punctuation">(</span>actions<span class="token punctuation">.</span><span class="token function">getBalance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">2000</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">fetWeb3Init</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">getBalance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>div className<span class="token operator">=</span><span class="token string">'App'</span><span class="token operator">></span> <span class="token operator"><</span>header className<span class="token operator">=</span><span class="token string">'App-header'</span><span class="token operator">></span> <span class="token operator"><</span>img src<span class="token operator">=</span><span class="token punctuation">{</span>logo<span class="token punctuation">}</span> className<span class="token operator">=</span><span class="token string">'App-logo'</span> alt<span class="token operator">=</span><span class="token string">'logo'</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span>p<span class="token operator">></span> <span class="token operator"><</span>b<span class="token operator">></span>Account<span class="token punctuation">:</span> <span class="token operator"><</span><span class="token operator">/</span>b<span class="token operator">></span> <span class="token operator"><</span>i<span class="token operator">></span><span class="token punctuation">{</span>account<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>i<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>p<span class="token operator">></span> <span class="token operator"><</span>br <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span>p<span class="token operator">></span>Balance<span class="token punctuation">:</span> <span class="token punctuation">{</span>balance<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>p<span class="token operator">></span> <span class="token operator"><</span>a className<span class="token operator">=</span><span class="token string">'App-link'</span> href<span class="token operator">=</span><span class="token string">'https://reactjs.org'</span> target<span class="token operator">=</span><span class="token string">'_blank'</span> rel<span class="token operator">=</span><span class="token string">'noopener noreferrer'</span> <span class="token operator">></span> Learn React <span class="token operator"><</span><span class="token operator">/</span>a<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>header<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">default</span> App<span class="token punctuation">;</span> |
Và để hoàn thiện phần cuối cùng chúng ta sẽ tạo thêm một form nho nhỏ để sendCoin, gồm 2 giá trị receiver và amount bằng cách sử dụng useState :
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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | <span class="token keyword">import</span> React<span class="token punctuation">,</span> <span class="token punctuation">{</span> useEffect<span class="token punctuation">,</span> useState <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'react'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> useDispatch<span class="token punctuation">,</span> useSelector <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'react-redux'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> logo <span class="token keyword">from</span> <span class="token string">'./logo.svg'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token string">'./App.css'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> actions <span class="token keyword">from</span> <span class="token string">'actions'</span><span class="token punctuation">;</span> <span class="token keyword">function</span> <span class="token function">App</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> dispatch <span class="token operator">=</span> <span class="token function">useDispatch</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> balance<span class="token punctuation">,</span> account <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useSelector</span><span class="token punctuation">(</span><span class="token punctuation">(</span>state<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span><span class="token punctuation">{</span> balance<span class="token punctuation">:</span> state<span class="token punctuation">.</span>balance<span class="token punctuation">,</span> account<span class="token punctuation">:</span> state<span class="token punctuation">.</span>account <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token punctuation">[</span>amount<span class="token punctuation">,</span> setAmount<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token punctuation">[</span>receiver<span class="token punctuation">,</span> setReceiver<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">useEffect</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">fetWeb3Init</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">await</span> <span class="token function">dispatch</span><span class="token punctuation">(</span>actions<span class="token punctuation">.</span><span class="token function">web3Connect</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">await</span> <span class="token function">dispatch</span><span class="token punctuation">(</span>actions<span class="token punctuation">.</span><span class="token function">instantiateContracts</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">function</span> <span class="token function">getBalance</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">setInterval</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">dispatch</span><span class="token punctuation">(</span>actions<span class="token punctuation">.</span><span class="token function">getBalance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">2000</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">fetWeb3Init</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">getBalance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>dispatch<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>div className<span class="token operator">=</span><span class="token string">'App'</span><span class="token operator">></span> <span class="token operator"><</span>header className<span class="token operator">=</span><span class="token string">'App-header'</span><span class="token operator">></span> <span class="token operator"><</span>img src<span class="token operator">=</span><span class="token punctuation">{</span>logo<span class="token punctuation">}</span> className<span class="token operator">=</span><span class="token string">'App-logo'</span> alt<span class="token operator">=</span><span class="token string">'logo'</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span>p<span class="token operator">></span> <span class="token operator"><</span>b<span class="token operator">></span>Account<span class="token punctuation">:</span> <span class="token operator"><</span><span class="token operator">/</span>b<span class="token operator">></span> <span class="token operator"><</span>i<span class="token operator">></span><span class="token punctuation">{</span>account<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>i<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>p<span class="token operator">></span> <span class="token operator"><</span>br <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span>p<span class="token operator">></span>Balance<span class="token punctuation">:</span> <span class="token punctuation">{</span>balance<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>p<span class="token operator">></span> <span class="token operator"><</span>label<span class="token operator">></span> Receiver<span class="token punctuation">:</span> <span class="token operator"><</span>input type<span class="token operator">=</span><span class="token string">'text'</span> value<span class="token operator">=</span><span class="token punctuation">{</span>receiver<span class="token punctuation">}</span> onChange<span class="token operator">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">setReceiver</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>label<span class="token operator">></span> <span class="token operator"><</span>label<span class="token operator">></span> Amount<span class="token punctuation">:</span> <span class="token operator"><</span>input type<span class="token operator">=</span><span class="token string">'text'</span> value<span class="token operator">=</span><span class="token punctuation">{</span>amount<span class="token punctuation">}</span> onChange<span class="token operator">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">setAmount</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>label<span class="token operator">></span> <span class="token operator"><</span>button onClick<span class="token operator">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">dispatch</span><span class="token punctuation">(</span>actions<span class="token punctuation">.</span><span class="token function">sendCoin</span><span class="token punctuation">(</span>receiver<span class="token punctuation">,</span> amount<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span> <span class="token operator">></span> Submit <span class="token operator"><</span><span class="token operator">/</span>button<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>header<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">default</span> App<span class="token punctuation">;</span> |
Kết qủa
Cuối cùng thì kết quả sẽ có dạng như sau :
Giao diện khá basic nhưng về cơ bản các function đã chạy khá ổn. Qua bài trên mình đã hướng dẫn khởi tạo một Dapp cơ bản với Reactjs , bài tiếp theo mình sẽ hướng dẫn xây dựng trên Vuejs và quản lý các state với Vuex
Các bạn có thể tham khảo code tại đây:
https://github.com/tranchien2002/EthCodeBased/tree/add-react