Dapp Front-end: Drizzle Store

A quick guide for using drizzle store in a React Dapp

wissal haji
The Startup
8 min readFeb 10, 2021

--

Photo by Samuel Yongbo Kwon on Unsplash

If you have been trying lately to build your dapp front-end with React and Redux, and you’ve realized that you couldn’t just focus on what the dapp is supposed to do, because you had to spend a lot of time configuring the application to provide the components with web3 and contracts instances and keep data synchronized with the blockchain, then this is the right place for you, drizzle store is exactly what you need in this case. So, let’s see how it works and how to use it with a dapp front-end.

What is drizzle store and how it works

The main purpose of drizzle store is to provide you with a ready to consume version of the redux store configured to manage everything related to web3 instance, contracts instances, events, transactions and calls.

In order to use the drizzle store, you need to build an instance of drizzle.
The drizzle instance is responsible for holding the following attributes:

  • Web3 instance
  • Contracts instances: An object containing drizzle contracts instances with contracts names as keys.
  • Contracts list: An array of drizzle contracts.
  • Redux store
  • Options: The options used for configuring the drizzle store.

The attributes specified in bold are the ones you’re going to use in your React components.
In order to instruct drizzle with how you would like the store to be managed and what data you would like to keep track of, you need to provide the drizzle instance with the appropriate options. You can find the full list of allowed options and their descriptions here.

The state of drizzle store

The drizzle store manages the following state:

Drizzle state: source

accounts / accountBalances: The list of accounts is fetched using web3.eth.getAccounts then their corresponding balances are obtained using web3.eth.getBalance and stored in the state during the process of initializing drizzle. If the accounts polling interval is defined in the options, the accounts and their balances will be fetched again whenever the time interval is reached.

contracts : This is where the contracts events and calls results are stored.
The synced property of a contract object is set to false if a new block is broadcasted and the contract is syncing, and set to true if the contract is synced (all contract calls are replayed).
When initializing a contract, the contract object built using the web3 instance ( new web3.eth.Contract()) is modified to add cacheCall and cacheSend methods on top of the call and send methods. Once this is done, all the events specified for that contract in the options are being subscribed to, and all incoming events will be added to the state under the events property of that contract.
The results of the calls are indexed with the arguments hash obtained when using cacheCall .

currentBlock : The latest block represented as an object resulting from web3.eth.getBlock().
During drizzle initialization, a saga event channel is created to listen for each newly incoming block using web3.eth.subscribe('newBlockHeaders', (error, result)=>{}) and save it in the state.
If syncAlways option is set to true, then all contracts calls are replayed when a new block is received.
If syncAlways option is set to false and if any of the contracts saved is concerned with one of the transactions present in the block, then all calls of the concerned contract are replayed.

drizzleStatus : An object containing information about the status of Drizzle.
The only property of this object is initialized which is set to true if the initialization process is completed (instantiating web3 and contracts, fetching accounts and their balances).

transactions : This is where transactions results are stored using either the transaction hash as a key or a temporary key if the transaction hash is not available yet. When the transaction is created, an event channel is associated with the transaction object obtained using [contractName].methods.[methodName] in order to be able to listen on events such as “transactionHash”, “confirmation”, “receipt” and “error”, then a temporary key is created and saved in transactionStack and the index of that key is returned as a result of the cacheSend method.

transactionStack : This is where the transactions keys are stored.
When the transaction is created and the transaction hash is not available yet a temporary key will be pushed to this stack, so that in case the transaction fails, the user can use this key to get the error message from the transactions object in the state. Once the transaction hash is available, the temporary key will be replaced with that hash.

web3 : An object that contains the status of the web3 instance, a networkId if it’s available, and a networkMismatch flag set to true if the user’s wallet network is not defined in the networkWhiteList option, if there is no network mismatch, then this property is not defined.
The possible statuses for web3 are: initializing , initialized and failed .

What happens when you build a drizzle instance

When you first build the drizzle instance, the constructor proceeds as follows:

  • First the options provided to the constructor are merged with the default options, which means if you don’t provide one of the options defined in the default ones, the default values are going to be used instead.
    The default options are as follows:
  • Once the environment is ready (meaning in case of a web browser the window object is available and loaded), the constructor dispatches DRIZZLE_INITIALIZING action to start the initialization process which is described in this flow chart:
Drizzle initialization process

As described in the flow chart above the initialization process starts by initializing the web3 instance first, and this is done using the web3 field of the options provided to the drizzle constructor.

The instantiation can be described as shown in the next diagram:

Web3 initialization

Calls and transactions

Calls:

When you want to read data from the ethereum blockchain, you can either use call from the web3 contract or cacheCall added by drizzle. The difference is that the cacheCall will return the args hash which is used as an index for the call result stored in the state which will be synchronized with the latest version of this data available in the blockchain, whereas call will return only the data available in the blockchain at the time of that call.

Transactions:

As it’s the case with calls, you can also either use send or cacheSend .
The cacheSend method returns the index of the key used to reference the result of the transaction which will be stored in the transactions object located in the state. The key can be retrieved from the transactionStack array using the index returned from cacheSend .

Adding and deleting contracts dynamically

Adding contracts

If you want to add a contract you can either use drizzle.addContract() or the ADD_CONTRACT action.

Deleting contracts

You can also delete contracts using either drizzle.deleteContract() or the DELETE_CONTRACT action.

Generate Store

If you want to customize drizzle store then you should use generateStore which will build the store instance and configure it to add your application reducers, sagas and middlewares. By default if you don’t specify the store in the drizzle constructor, drizzle will create its own one.

generateStore takes an object with the following properties:

  • drizzleOptions : The options used for configuring drizzle store.
  • appReducers : An object containing all application reducers that will be added to drizzle reducers using combineReducers from Redux.
  • appSagas : An array containing the application sagas.
  • appMiddlewares : An array containing middlewares you want to add to the store.

The only property that is required is drizzleOptions , the other ones have a default value.

Example: Simple Storage

In this example we will build a simple dapp that will read data from contract storage and update it.

1. Create the truffle project and deploy the contract

Start by creating an emty project in your directory called drizzle-example, and use truffle init to initialize the project.

Open the project with VScode or your favorite code editor, and create the SimpleStorage contract with its migration file.
Update the truffle-config.js file with ganache as a development network and set the compiler version, then add the following content to the SimpleStorage contract:

Open the truffle console, then compile and deploy the contract.

2. Initialize the React application

In the same project create a new folder called client with create-react-app.

Add the dependencies : web3, drizzle store, redux, react-redux, redux-logger

Create three new folders under client/src drizzle and components:

Go back to truffle-config.js file and add this line under module.exports:

You also need to import the path module in the begining of the file:

Now you need to redeploy your project in order to have the contract’s json interfaces available in the contracts folder.

3. Configure drizzle:

Under the drizzle folder, create two files: drizzleOptions.js and drizzleContext.js
In order to configure the application we’re going to need two things from drizzle:

  • The drizzle instance which will provide us with web3 and contracts instances.
  • The drizzle store.

In order to make the drizzle instance available in the components we will use the React Context API:

In the drizzleOptions.js file we’re going to add the following configuration:

4. Connect the application with drizzle store

First, we’re going to add the redux-logger middleware to the store which will be provided to the drizzle instance, then we will use the drizzle provider we’ve built in drizzleContext.js file and redux provider to provide the application with the store and the drizzle instance.
Here is the index.js file:

5. Create the Loading container component

Under the components folder create a new file called LoadingContainer.js:

Once you’ve created the LoadingContainer component you need to add it in your index.js file:

6. Interact with the contract
Now it’s time to modify the App.js file:

That’s it, you can now try it out in your browser.

You can find the full project here on Github.

--

--