Как интегрировать пакет синхронизации цепочек и состояний Truffle, Drizzle, в ваши проекты React

Больше не нужно беспокоиться о состоянии цепочки синхронизации?

Пакет Drizzle является частью набора инструментов Truffle для разработки интерфейсных приложений (децентрализованных приложений) на основе Ethereum. Дождь направлен на решение государственной проблемы; проблема синхронизации данных цепочки с состоянием приложения и, следовательно, актуальности вашего пользовательского интерфейса для конечного пользователя.

До недавнего времени разработчикам приходилось искать лучший подход для синхронизации активности в сети - состояния контракта, событий и транзакций - со своими децентрализованными приложениями через vanilla web3. С пакетом Drizzle это дополнительное внимание больше не нужно. Drizzle подписывается на новые заголовки блоков и отслеживает изменения в состоянии контракта - контракты, которые вы специально даете Drizzle для прослушивания. Затем изменения состояния автоматически сохраняются в хранилище Redux.

Таким образом, Drizzle особенно хорошо работает с React. Эта статья призвана познакомить вас с Drizzle; мы установим Drizzle Tr uffle Box, исследуя возможности проекта в его текущем воплощении, а также готовые компоненты, предоставленные нам разработчиками Drizzle.

Drizzle является частью Truffle Suite, поэтому некоторые базовые общие знания о пакете будут полезны при чтении этой статьи. Прочтите мою вводную статью, чтобы понять конвейер разработки децентрализованных приложений применительно к пакету Truffle:



Коробка с трюфелем Мороси

Самый быстрый способ начать работу с Drizzle - это загрузить Drizzle Truffle Box, пакет, состоящий из среды Create React App в сочетании с drizzle, drizzle-react и drizzle-react-components, что дает нам доступ к полному набору возможностей Drizzle в загружаемом приложении React. .

Вам нужно использовать React, чтобы использовать Drizzle? Нет - и именно поэтому интерфейс Drizzle разбит на 3 пакета, что дает разработчикам возможность интегрировать его в любой интерфейсный фреймворк, который они используют.

Давайте быстро разберемся с назначением этих трех пакетов:

  • drizzle: Основная функциональность размещения «реактивного хранилища данных» для web3 и смарт-контрактов. Реактивное хранилище данных означает хранилище Redux, которое автоматически обновляется при внесении изменений в состояние вашего смарт-контракта. Любое приложение Javascript, которое приняло (или планирует принять) контейнер состояния Redux, может продолжить и потребовать дождя в качестве зависимости.
  • drizzle-react: абстрагирует основные функции Drizzle до используемых компонентов React. Drizzle использует Context API React для упрощения интеграции через ваше дерево компонентов. Подробнее о контексте далее.
  • drizzle-react-components: Это не требование для работы Drizzle, а набор полезных компонентов пользовательского интерфейса, которые могут пригодиться во время разработки, в настоящее время состоящих из компонентов LoadingContainer, ContractData и ContractForm.

На этом этапе стоит сделать небольшой экскурс в Context in React, чтобы понять, как drizzle-react ожидает от нас интеграции объекта drizzle в наши проекты. Давай сделаем это дальше.

Объяснение контекста Quick React

Контекстный API React (React 16.3 и выше) позволяет передавать объекты вниз по дереву компонентов, не встраивая их в качестве свойств во все дерево компонентов. Вместо этого мы оборачиваем это дерево компонентом контекста, делая объекты контекста доступными во всей иерархии.

Представьте себе сценарий, в котором мы передаем одно строковое значение двум или более компонентам. Просто передать одно значение через дерево компонентов - утомительная работа. Теперь давайте посмотрим, как поставщик контекста может решить эту проблему:

const ThemeContext = React.createContext('light');
class App extends React.Component {
  //Wrap your component tree with a Context provider.
  render() {
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}
function Toolbar(props) {
  return (
    <div><ThemedButton /></div>
  );
}
//Children of your Context provider now have access to provider value
class ThemedButton extends React.Component {
  static contextType = ThemeContext;
  render() {
    return <Button theme={this.context} />;
  }
}

Прежде всего, объект контекста определяется со значением по умолчанию: light. Это значение может быть расширено до объекта, массива или любого допустимого типа, который вам нужен. Например:

const NameOfContext = React.createContext({theme: 'light'});

Затем в наше дерево компонентов можно включить специальный Provider подкомпонент нашего объекта контекста, а также значение, заменяющее значение контекста по умолчанию, которое мы определили выше:

<NameOfContext.Provider value={theme: 'dark'}">
    <ComponentTree />
</NameOfContext.Provider>

Теперь в любом месте нашего дерева компонентов мы можем ссылаться на нужный нам контекст (поскольку в нашу иерархию может быть встроено множество контекстов) через статический объект contextType и получить доступ значение контекста так:

class DeepTreeComponent extends React.Component {
  static contextType = NameOfContext;
  render() {
    return <Button theme={this.context.theme} />;
  }
}

Официальные документы React предоставляют более подробное объяснение контекста, а также альтернативы контексту, когда можно использовать лучшее решение. Посетите эту страницу здесь.

Контекст применен к Мороси

Итак, как это знание контекста применимо к дождю? Итак, объект drizzle сначала нужно инициализировать в нашем корневом компоненте (или самом верхнем слое, для которого вы хотите, чтобы моросящий дождь был доступен). Отсюда было бы неплохо, если бы все наше приложение имело доступ к объекту drizzle - здесь в игру вступает контекст:

...
const drizzle = new Drizzle(options, drizzleStore);
ReactDOM.render(
  <DrizzleContext.Provider drizzle={drizzle}>
    <App />
  </DrizzleContext.Provider>,
  document.getElementById('root'));

Теперь мы знаем, из чего состоит блок Drizzle, и как мы можем использовать контекст, чтобы сделать Drizzle доступным в нашем приложении React. Давайте продолжим наш разговор, установив коробку «Моросящий трюфель».

Коробка с трюфелем Мороси

Intsallation

Перейдем к установке мороси из окна Drizzle. Для этого выполните следующие команды:

#install truffle and ganache cli if you have not done so
npm install -g truffle
npm install -g ganache-cli
#unpack the drizzle truffle box
truffle unbox drizzle
#change ganache recommended block time:
ganache-cli -b 3

Почему мы изменили время блока на 3 секунды? Потому что мы хотим имитировать более реалистичную цепочку блоков для обмена данными между приложениями. Ганаш по умолчанию мгновенно заминирует блоки; любые индикаторы загрузки или индикаторы ожидающих транзакций просто мгновенно исчезнут в нашем UX. Нам не нужно полное 15-секундное время блока, это занимает слишком много времени, но мы хотим иметь возможность тестировать все разрабатываемые нами компоненты. Для этого достаточно 2–3 секунд.

Как видите, здесь была вызвана опция Ganache CLI -b для изменения конфигурации непосредственно из Терминала. Для получения дополнительной информации о ganache-cli ознакомьтесь с полным списком команд здесь.

Импорт ваших контрактов

Чтобы начать использовать Drizzle, нам нужны смарт-контракты для взаимодействия. Есть два метода, которые мы можем использовать для загрузки смарт-контрактов в наше децентрализованное приложение:

  1. Импортируя перенесенные интерфейсы JSON контракта. В Truffle запуск truffle migrate (процесс развертывания ваших смарт-контрактов из /contracts в блокчейн) также создаст дополнительную build/contracts/ папку с интерфейсами JSON ваших контрактов. Это файлы, которые нам нужно импортировать.
  2. Путем динамического добавления контрактов через их публичный адрес и интерфейс JSON.
    Примечание. Если владелец подтвердил адрес своего контракта на Etherscan, вы можете скопировать JSON-интерфейсы указанного контракта, что теоретически позволяет нам экспериментировать с любым смарт-контрактом. развернуты в блокчейне, размещенном на платформе.

Используя метод 1, мы можем импортировать контракты и таким образом инициализировать изморозь в index.js.

index.js:

import React, { Component } from 'react';
//import drizzle dependencies
import { Drizzle, generateStore } from "drizzle";
import { DrizzleContext } from "drizzle-react";
//import contracts
import SimpleStorage from './../build/contracts/SimpleStorage.json';
import TutorialToken from './../build/contracts/TutorialToken.json';
//define drizzle options. Include contracts here.
const options = { contracts: [SimpleStorage, TutorialToken] };
//initialise drizzle store with options
const drizzleStore = generateStore(options);
//initialise drizzle object, passing options and store
const drizzle = new Drizzle(options, drizzleStore);
//include drizzle Context wrapping <App />
ReactDOM.render(
 <DrizzleContext.Provider drizzle={drizzle}>
    <App />
 </DrizzleContext.Provider>,
document.getElementById('root'));

Теперь Drizzle инициализируется на верхнем уровне вашего децентрализованного приложения, благодаря чему каждый компонент теперь будет иметь доступ к объекту контекста Drizzle.

Теперь давайте перейдем к <App />. Провайдер контекста дождя дает нам 3 объекта; а именно drizzle, drizzleStore и initialised, что дает нам доступ к функциям дождя, состоянию дождя и тому, готов ли дождь соответственно. «Готово» здесь означает, что изморось инициализирована и подключена к провайдеру web3.

Давайте посмотрим, как выглядит доступ к этим объектам в <App />.

App.js:

import { MyDrizzleApp } from './MyDrizzleApp';
import { DrizzleContext } from "drizzle-react";
export const App = () => (
//DrizzleContext Consumer gives us access to our drizzle objects.<DrizzleContext.Consumer>
    {drizzleContext => {
      const { drizzle, 
              drizzleState, 
              initialized } = drizzleContext;
     //return a loading UI if drizzle is not yet initialised
     if(!initialized){
        return "Loading...";
      }
      
      //pass drizzle down as props into a subcomponent
      <MyDrizzleApp 
         drizzle={drizzle} 
         drizzleState={drizzleState} />
  }}
  </DrizzleContext.Consumer>
)
export default App;

Обратите внимание, как drizzleStore теперь изменилось на drizzleState внутри потребителя контекста.

Выше показано, как получить доступ к мороси из <DrizzleContext.Consumer>. Пользовательский интерфейс загрузки возвращается в том случае, если изморось еще не инициализирована.

Скорее всего, ваше децентрализованное приложение не будет работать без инициализации измороси, поэтому я рекомендую полноэкранный предварительный загрузчик с теневой анимацией заполнителя там, где это необходимо.

Примечание. Этот <MyDrizzleApp /> пример взят из официальной документации Drizzle. Забавно, как контекст моросящего дождя позволяет получить доступ к дождю в любом месте дерева компонентов, первый пример использования которого сразу же обращается к реквизитам. Но это, по крайней мере, подчеркивает наши возможности обводить компоненты изморосью.

Давайте, наконец, извлечем drizzle реквизиты в <MyDrizzleApp />:

MyDrizzleApp.js:

import React from "react";
export default class MyDrizzleApp extends React.Component {
  
 componentDidMount() {
    //access drizzle props within componentDidMount
    const { drizzle, drizzleState } = this.props;
 }
render() {
    return (
      <div>...</div>
    );
  }
}

componentDidMount - предпочтительный метод для вызова контрактов и подготовки данных компонентов, полученных из web3. Теперь у нас под рукой drizzle и drizzleState, давайте рассмотрим, как получать и устанавливать данные из смарт-контрактов в цепочку блоков, а также как инициировать транзакции. Это делается двумя способами: cacheCall() и cacheSend().

Взаимодействие с контрактами

cacheCall() и cacheSend() - это два метода, которые обеспечивают не только методы смарт-контракта и выборки данных, но и сохраняют эти данные ответа в drizzleState. После этого эти данные синхронизируются с блокчейном.

Давайте кратко рассмотрим 2 примера этих методов.

cacheCall () отправляет данные контракта и сохраняет результаты в состоянии

Вызов cacheCall() вызовет метод смарт-контракта и сохранит возвращенные данные в drizzleState. Ключ для доступа к этим данным также предоставляется после разрешения cacheCall(), а именно dataKey. Предположим, что в MyDrizzleApp инициализируется изморось, и вызовем контрактный метод:

const getStoredData = () => {
   let state = drizzle.store.getState();
   //1. make the call to the `storedData` method of our SimpleStorage contract. dataKey is returned.
   const dataKey = drizzle.contracts.SimpleStorage.methods.storedData.cacheCall();
   //2. return the resulting state value using dataKey.
   return    state.contracts.SimpleStorage.methods.storedData[dataKey].value;
}

Важно помнить, что этот вызов продолжит синхронизацию наших данных с блокчейном. Если мы просто хотим получить данные, не сохраняя их в drizzleState, и, следовательно, не синхронизировать их, у нас все еще есть доступ к обычным методам и свойствам web3 в измороси:

drizzle.contracts.SimpleStorage.methods.storedData().call();

Если cacheCall() разрешает взаимодействие по контракту, cacheSend() позволяет нам вызывать и синхронизировать транзакции по контракту. Давайте посмотрим на это дальше.

cacheSend () отправляет транзакцию и сохраняет результаты в состоянии

Вызов cacheSend() инициирует транзакцию. Передача параметров в этот метод позволяет нам предоставить стандартные from, gas и gasPrice транзакции. Drizzle предоставляет stackId ключ, используемый для доступа к статусу транзакции и другим данным в drizzleState.

Давайте посмотрим, как транзакцию можно вызывать и получать из состояния с помощью cacheSend():

const sendTransaction = () => {
   let state = drizzle.store.getState();
   //send 2 ETH in a transaction calling the `set` method of SimpleStorage.
   const stackId = drizzle.contracts.SimpleStorage.methods.set.cacheSend(2, {from: '0x3f...'});
   //using stackId, access the transaction hash and status within drizzleState.
   if(state.transactionStack[stackId]){
      const txHash = state.transactionStack[stackId];
      return state.transactions[txHash].status;
   }
}

Как и cacheCall(), у нас также есть доступ к методам web3 на тот случай, если мы не хотим, чтобы эта транзакция синхронизировалась с магазином:

drizzle.contracts.SimpleStorage.methods.set(2).send({from: '0x3f...'})

Подробную документацию по контрактному взаимодействию можно найти здесь в официальных документах Truffle.

Наличие существующего магазина Redux

Возможно, вы уже инициализировали redux store до добавления Drizzle в ваше приложение. Это не проблема; В Drizzle содержится подробная документация о том, как запустить drizzleStore inline в вашем существующем магазине, а также о том, как объединить drizzleStore в существующий магазин. Предпочтительный метод будет зависеть от ваших требований к децентрализованному приложению, но я бы предпочел объединить состояние в один магазин. Полную документацию по этой теме можно найти здесь.

Где взять Дождь отсюда

Мы узнали, как интегрировать Drizzle в ваше децентрализованное приложение с помощью Context, а также как вызывать методы контракта и отправлять транзакции. Это можно сделать с помощью web3 или с помощью методов cacheCall() и cacheSend() drizzle, которые автоматически сохраняют результаты в текущем состоянии и синхронизируют данные.

Конечно, это еще не все, что может предложить Drizzle - прочтите документацию полностью (начиная с обзора), чтобы полностью ознакомиться с Drizzle. В частности, обратите внимание на состояние структуры и опции дождя, чтобы получить полное представление о его возможностях.

Мои мысли о Дождле

Drizzle стремится решить критическую функцию синхронизации ваших децентрализованных приложений с сетью (блокчейн) и делает это таким образом, чтобы разработчикам не нужно было думать о внутренней работе пакета. Drizzle удобен для разработчиков; это позволяет нам больше сосредоточиться на поведении UX, не беспокоясь о том, как синхронизировать истинные данные из блокчейна; это делается за кулисами и поставляется в вашем магазине мороси. Это огромная экономия времени, поскольку мы уверены, что Drizzle надежен.

drizzle-react-components все еще находится на начальной стадии, и в настоящее время из пакета доступны только 3 компонента; Я уверен, что предложение будет расти, как только новые сценарии использования будут разработаны и включены в пакет; время исправит это. Тем не менее, компонент ‹ ContractForm / › очень полезен, так как предоставляет вам форму на лету для взаимодействия с вашими контрактами. На данный момент это основное преимущество пакета, и он отлично подходит для тестирования в вашем интерфейсе.

И, естественно, Drizzle - долгожданное дополнение к набору Truffle, которое можно интегрировать в ваш проект без каких-либо изменений в существующей конфигурации Truffle. Общий дождь - сильное дополнение к набору трюфелей.