@vocdoni/react-hooks

A set of React hooks for UI clients making use of the Vocdoni ecosystem, a universally verifiable, privacy-centric and scalable digital voting protocol.

Usage no npm install needed!

<script type="module">
  import vocdoniReactHooks from 'https://cdn.skypack.dev/@vocdoni/react-hooks';
</script>

README

Vocdoni React Hooks

A set of React hooks for UI clients making use of the Vocdoni ecosystem, a universally verifiable, privacy-centric and scalable digital voting protocol.

Get started

npm i @vocdoni/react-hooks

The packages dvote-js, use-wallet and ethers are also required.

Providers

At the root of your project, add the following providers:


import React, { useEffect, useState } from 'react'
import { UsePoolProvider, UseProcessProvider, UseEntityProvider, usePool, useProcess, useProcesses, useEntity } from '../.'

const BOOTNODE_URI = "https://bootnodes.vocdoni.net/gateways.dev.json"
const ENVIRONMENT = "dev" 
const NETWORK_ID = "rinkeby"
const CHAIN_ID = 4

const MyApp = () => {
  return (
    <UsePoolProvider bootnodeUri={BOOTNODE_URI} networkId={NETWORK_ID} environment={ENVIRONMENT}>
      <UseProcessProvider>
        <UseEntityProvider>
          <UseBlockStatusProvider>
            
            // ...
            <MyMainComponent /> 

          </UseBlockStatusProvider>
        </UseEntityProvider>
      </UseProcessProvider>
    </UsePoolProvider>
  )
}

While not all of them are needed, the Process hooks depend on UsePoolProvider being avilable.

Hooks

Gateway Pool

To get a Gateway Pool:

const PoolComponent = () => {
  const { pool, poolPromise, loading, error, retry, refresh } = usePool()

  useEffect(() => {
    poolPromise
      .then(pool => { // Do something with the gateway pool instance
        return VotingApi.getResultsDigest(myProcessId, pool)
      })
      .then(console.log) // results
      .catch(console.error) // err
  }, [])

  return <div>
    <h2>Gateway Pool</h2>
    <p>The Gateway Pool is {loading ? "being loaded" : "ready"}</p>
    {pool ? <p>Ready</p> : null}
    {error ? <p>Error: {error}</p> : null}
    {error && pool ? <p><button onClick={refresh}>Refresh</button></p> : null}
    {error && !pool ? <button onClick={retry}>Retry</button> : null}
  </div>
}

The poolPromise object is guaranteed to be a non-null Promise, resolving to a valid Gateway Pool instance, as many times as it is evaluated.

Using pool is discouraged, use poolPromise instead.

Process info (single)

To get a cached version of a process' details:

const processId = "0x1234..."

const ProcessComponent = () => {
  const { process, loading, error } = useProcess(processId)

  return <div>
    <h2>Process</h2>
    <p>The Process details are {loading ? "being loaded" : "ready"}</p>
    {
      !process ? null : <>
        <pre>Process ID: {process.id}</pre>
        <pre>Entity Address: {process.entity}</pre>
        <pre>{JSON.stringify(process.metadata, null, 2)}</pre>
        <pre>{JSON.stringify(process.parameters, null, 2)}</pre>
      </>
    }
    {error ? <p>Error: {error}</p> : null}
  </div>
}

Process info (list)

const processIds = ["0x1234...", "0x2345..."]

const ProcessesComponent = () => {
  const { processes, loading, error } = useProcesses(processIds)

  // processes is a Map<string, ProcessInfo>

  return <div>
    <h2>Processes</h2>
    <p>The Process details are {loading ? "being loaded" : "ready"}</p>
    <ul>
      {
        processIds.map(id => {
          const processInfo = processes.get(id)
          if (!processInfo) return <li key={id}>Loading {id.substr(0, 6)}...</li>
          return <li key={id}>{processInfo.metadata.title.default}</li>
        })
      }
    </ul>
    {error ? <p>Error: {error}</p> : null}
  </div>
}

Entity metadata

To get a cached version of an entity's metadata:

const entityId = "0x1234..."

const EntityComponent = () => {
  const { metadata, loading, error } = useEntity(entityId)

  return <div>
    <h2>Entity</h2>
    <p>The entity details are {loading ? "being loaded" : "ready"}</p>
    {
      !metadata ? null : <>
        <pre>Entity ID: {entityId}</pre>
        <pre>{JSON.stringify(metadata, null, 2)}</pre>
      </>
    }
    {error ? <p>Error: {error}</p> : null}
  </div>
}

Date/block estimation

const DateBlockComponent = () => {
  const targetBlock = 123
  const targetDate = new Date(2030, 10, 10, 10, 10)

  // Two-way estimation
  const { blockHeight } = useBlockHeight()
  const { blockStatus } = useBlockStatus()
  const { date, loading, error } = useDateAtBlock(targetBlock)
  const { blockHeight: estimatedBlockNumber } = useBlockAtDate(targetDate)

  return <div>
    <h2>Date/block estimation</h2>
    <p>The block status details are {loading ? "being loaded" : "ready"}</p>
    {blockHeight ? <p>Current block {blockHeight}</p> : null}
    {!date ? null : <p>Date at block {targetBlock}: {date.toJSON()}</p>}
    {estimatedBlockNumber ? <p>Block on {targetDate.toJSON()}: {estimatedBlockNumber}</p> : null}
    {!blockStatus ? null : <pre>{JSON.stringify(blockStatus, null, 2)}</pre>}
    {error ? <p>Error: {error}</p> : null}
  </div>
}

Example

Check out example/index.tsx for a working example.

cd example
npm install
npm start