I integrated the next.js framework into my project and created one basic view this week.
Next.js is a framework for setting up a website powered by react with server-side rendering. The difference between Next and something like Create React App is that the server runs the javascript locally to generate a bundle of static html files that are then sent to the requesting client first, before the javascript bundle, which is sent after. In CRA the whole bundle is sent first, and the html pages are generated in the browser by the client. This accomplishes 2 things. 1: The first page load is much quicker for the client because they are first receiving just one static html page that corresponds to the route in their url. 2: We are able to do some explicit manipulation on the server-side rather than assuming everything is being rendered in the client's browser.
The second factor is the most important one for developing an Etherium application. One assumption we have to make for users to interract with our application from the client side is that they are running the metamask browser extension that let's them manage an account to interract with the etherium blockchain. If we were rendering everything client-side, if somebody was not logged in to metamask or did not have an account, our website would do almost nothing. There would be no way to view the rockPaperScissors games available or start a new game (deploy a new game contract using the factory). However, utilizing server-side rendering and our own connection to the etherium blockchain through an infura node, we can at least fetch and display a lot of the relevant data without the client actively running metamask. They still won't be able to interract with the blockchain but they will be able to at least see what our application is about.
To create our first view we added three files to our project: factory.js, web3.js and index.js. The first file we set up is web3.js.
import Web3 from 'web3';
let web3;
if (typeof window !== 'undefined' && typeof window.web3 !== 'undefined') {
// we are in browser
web3 = new Web3(window.web3.currentProvider)
} else {
// we are on the server or user is not using metamask
const provider = new Web3.providers.HttpProvider(
'https://rinkeby.infura.io/v3/9319195d62034106b0e7b86277444e17'
);
web3 = new Web3(provider)
}
export default web3
The if statement is a check for whether or not the code is being run on the browser and whether or not the user is logged in to metamask. If they aren't, or the code is being run on the server, then we set our web3 instance to hook up to the etherium network through the infura node. If they are using metamask then we use their account (window.web3.currentProvider).
The next file is factory.js:
import web3 from './web3';
import RockPaperScissorsFactory from './build/RockPaperScissorsFactory.json'
const instance = new web3.eth.Contract(
JSON.parse(RockPaperScissorsFactory.interface),
'0xE6168ab7c83F3904a949ddD6bA3Aad0CF969C461'
);
export default instance;
This file is creating a new instance of a web3 contract. It is parsing the interface or ABI from our compiled factory contract in our build folder, and being passed the address of a deployed contract to interact with. In this case we got the address from the return value after running our deploy script.
The last file is index.js:
import React, { Component } from 'react';
import factory from '../etherium/factory';
class RockPaperScissorsIndex extends Component {
static async getInitialProps(){
const games = await factory.methods.getDeployedRockPaperScissors().call();
return {games}
}
render() {
console.log(this.props.games)
return(
<div>
{this.props.games}
</div>
)
}
}
export default RockPaperScissorsIndex
In this file we are importing our factory instance so we can make calls to our factory contract. The getInitialProps() method is built in to the next.js framework. It is a function that is automatically called before rendering the component. It then passes it's return value as props to the parent component. This facilitates server-side rendering by allowing us to fetch data before any client-side rendering takes place. In this method we are calling the getDeployedRockPaperScissors method from our deployed factory contract, which returns an array of the addresses of all the deployed rockPaperScissors contracts. We are then rendering this.props.games inside our component, which is that returned array of deployed contract addresses.
Next.js has some very convenient built-in routing properties. We created a pages folder inside our root directory. Everything in that pages folder get's automatically rendered at the address of the filename. For instance, in development, /pages/show will be rendered at localhost:3000/show, and /pages/index gets automatically rendered as the homepage at localhost:3000/.
Seeing as we haven't created a way for users to interract with our factory and deploy new rockPaperScissors contracts yet, we had to create some dummy data to display. To do this, we used our remix IDE, found our contract at the specified address, and used the injected web3 interface to deploy a rockPaperScissors game.
I had another versioning issue. For whatever reason there was an error in the version of the web3 beta I was using the returned this error every time I tried to interract with my deployed contract: Error: Returned values aren't valid, did it run Out of Gas? To fix this issue I had to revert to version 1.0.0-beta.35 from 1.0.0-beta.37. I found the fix at this github issue