Ethereum: Creating a Block Explorer with React (Part 2)

Listing Recent Blocks in React

This article continues the development of an Ethereum block explorer built in React. I’ll continue right from where I left off in part one, so get ready. I hope you enjoy it.

Previously…

Check out these previous articles which describe setting up the dev environment and the first part of the app:

Getting started

Change directory into the project folder blockexp and start the development server:

cd blockexp
npm start

Firefox should automatically launch with the the app loaded:

create-react-app default

Next we need to start geth. I’ve previously setup a private blockchain as described in my article here.

Note that the React development server is already running on port 3000, so make sure that geth is running on a different port. I’ll be using 3020.

Open a new terminal and change into the directory that contains privchain. Now start geth:

geth --port 3020 --networkid 58342 --nodiscover --datadir="privchain" --maxpeers=0 --autodag \
         --rpc --rpcport 8545 --rpcaddr 127.0.0.1 --rpccorsdomain "*" --rpcapi "eth,net,web3" \
         --ipcapi "eth,net,web3"

Adding web3 in React

Previously we used React-Router to map the root path / to a component called Home, which is defined in components/Home/index.js. It should look like this:

import React, { Component } from 'react';
import './style.css';
class Home extends Component {
  render() {
    return (
      <div className="Home">
        <h2>Home page</h2>
      </div>
    );
  }
}
export default Home;

This component will act as the home page of the block explorer. Edit the file and add the below to import web3:

import Web3 from 'web3';

We also need to instantiate web3 with the node connection details. I’ll be using geth listening on port 8545. Add this below the imports:

var web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));

Now add some logging into the start of the render() function to make sure everything is working:

render() {
	console.log(web3.eth.accounts);
	return (
		...

In Firefox open the Web Console by hitting Ctrl + Shirt + k, then refresh the page. If all is working you should see the Ethereum account list from geth in the console. We can also interact with web3 from this console by instantiating a new object:

var web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));

Now you should be able to use web3 from the Firefox Web Console, for example:

web3.eth.blockNumber; // print the current block number

create-react-app default

Displaying the current block number

OK, now for the fun stuff. Edit components/Home/index.js again and add some new functions. Read more about these functions and the React component lifecycle here: https://facebook.github.io/react/docs/react-component.html

Add a contructor function inside the existing Home class:

class Home extends Component {
  constructor(props) {
    super(props);
    this.state = {
      block_ids: [],
      block_hashes: [],
      curr_block: null
    }
  }
  ...

In the constructor we’re setting up the state the app will use:

  • block_ids is an empty array to store block IDs
  • block_hashes is an empty array to store block hashes
  • curr_block will store the current block ID.

You can read more about React states here: https://facebook.github.io/react/docs/state-and-lifecycle.html

Now add another function below the constructor:

componentWillMount() {
  console.log(web3.eth.accounts);
  var curr_block_no = web3.eth.blockNumber;
  console.log(curr_block_no);
  this.setState({
    curr_block: curr_block_no
  });
}

This function uses web3 to read the current block number from geth and store in the state variable curr_block.

Next, update the render function to display the block:

render() {
  return (
    <div className="Home">
      <h2>Home page</h2>
        Current Block: {this.state.curr_block}
    </div>
  );
}

If all has worked Firefox should reload and show the current block number:

create-react-app default

Listing recent blocks

Next we’ll list the 10 most recent blocks. Edit components/Home/index.js again and import lodash. We’ll also import Link:

import _ from 'lodash';
import { Link } from 'react-router-dom'

Now add another new function below componentWillMount():

getBlocks(curr_block_no) {
  const block_ids = this.state.block_ids.slice();
  const block_hashes = this.state.block_hashes.slice();
  var max_blocks = 10;
  if (curr_block_no < max_blocks) max_blocks = curr_block_no;
  for (var i = 0; i < max_blocks; i++, curr_block_no--) {
    var currBlockObj = web3.eth.getBlock(curr_block_no);
    block_ids.push(currBlockObj.number);
    block_hashes.push(currBlockObj.hash);
  }
  this.setState({
    block_ids: block_ids,
    block_hashes: block_hashes
  })
}

This function, getBlocks(), accepts a single argument, curr_block_no, which will be the current block number. Call this function at the end of the componentWillMount function, right before the closing curly bracket:

componentWillMount() {
  ...
  this.getBlocks(curr_block_no);
}

I’ll try explain the getBlocks function a little. First, it creates a new copy of the existing state variables block_ids and block_hashes using slice(). Then some housekeeping ensures max_blocks doesn’t exceed curr_block_no. Next, it loops and uses web3 to get the block details of the last 10 blocks. Finally, we update the arrays in state.

Now we’ll use lodash to display the data on the page. Add the following inside the top of the render function, before the return statement:

var tableRows = [];
_.each(this.state.block_ids, (value, index) => {
  tableRows.push(
    <tr key={this.state.block_hashes[index]}>
      <td className="tdCenter">{this.state.block_ids[index]}</td>
      <td><Link to={`/block/${this.state.block_hashes[index]}`}>{this.state.block_hashes[index]}</Link></td>
    </tr>
  )
});

Finally, add a table to display the data. Enter the following in the return section of the render function. The full return section should look like this:

return (
  <div className="Home">
    <h2>Home page</h2>
    Current Block: {this.state.curr_block}
    <table>
      <thead><tr>
        <th>Block No</th>
        <th>Hash</th>
      </tr></thead>
      <tbody>
        {tableRows}
      </tbody>
    </table>
  </div>
);

That’s it. Check Firefox to make sure the page has reloaded successfully with no errors. It should look something like this:

create-react-app default

Clicking on any of the block hash links should load a page saying Block Info:

create-react-app default

Wrapping up

That’s it for this part. In the next part, the block page will be built out to display details of the selected block.