Library for Reactive Dapp Development with auto syncing and caching capabilities
A new set of components were created to simplify the usage of Subspace within React projects. Learn more about them here https://subspace.embarklabs.io/react.html
You can install it through npm
or yarn
:
npm install --save @embarklabs/subspace-react web3 rxjs
To use most of the subspace-react
components, you need to wrap your app with the <SubspaceProvider web3={web3} />
component. This will make Subspace available to any nested components that accesses it via the useSubspace
hook or has been wrapped in the withSubspace
higher order component. Any React component might use Subspace so it makes sense to add the provider near the top level of your dApp. The SubspaceProvider
requires a web3 object
// index.js
import React from 'react'
import ReactDOM from 'react-dom'
import MyApp from './MyApp'
import { SubspaceProvider } from '@embarklabs/subspace-react';
const web3 = new Web3("ws://localhost:8545");
const rootElement = document.getElementById('root')
ReactDOM.render(
<SubspaceProvider web3={web3}>
<MyApp />
</SubspaceProvider>,
rootElement
);
Rather than relying on global variables or passing Subspace through props, The easiest way to access Subspace features is via the useSubspace
hook. Be sure that your entire dApp is wrapped with a <SubspaceProvider />
to have it available througout the component tree.
// index.js
import React from 'react'
import { useSubspace } from '@embarklabs/subspace-react';
const MyComponent = () => {
const subspace = useSubspace();
// do something....
// subspace.trackBalance(web3.eth.defaultAccount);
return ...;
}
export default MyComponent
This higher order component is provided as an alternative to the useSubspace
hook. This injects the subspace
property with an already initialized Subspace instance. Just like with the hook, your entire dApp needs to be wrapped with a <SubspaceProvider />
.
// index.js
import React from 'react'
import { withSubspace } from '@embarklabs/subspace-react';
const MyComponent = (props) => {
// do something....
// props.subspace.trackBalance(web3.eth.defaultAccount);
return ...;
}
export default withSubspace(MyComponent);
Useful to make your component subscribe to any observable props it receives when the component is mounted and automatically unsubscribes when the component is unmounted. It can be used with any kind of observables. This component already existed in the previous version of Subspace. The only difference is that it has been moved to the subspace-react
package.
import { observe } from '@embarklabs/subspace-react';
const ObserverComponent = observe(WrappedComponent);
const MyComponent = ({eventData}) => {
// Handle initial state when no data is available
if (!eventData) {
return <p>No data</p>;
}
return <p>Value: {eventData.someReturnValue}</p>
};
const MyEnhancedComponent = observe(MyComponent);
const SomeOtherComponent = () => {
const myObservable$ = MyContractInstance.events.MyEvent.track({fromBlock: 1});
return <MyEnhancedComponent myProp={myObservable$} />;
}
web3
and rxjs
as peer depenencies, while before they were installed as dependencies of the packagenpm install --save @embarklabs/subspace web3 rxjs
Web3
object:const web3 = new Web3("http://localhost:8545");
const subspace = new Subspace(web3);
init()
is async
.
This means that you need to wait until Subspace has been initialized to use its features:await subspace.init();
The observe
higher-order component was moved from the @embarklabs/subspace
package to the new @embarklabs/subspace-react
package.
If no call interval is specified in the Subspace options, it will use the average blocktime as the interval of time to poll the contract addresses for changes in state or balance.
web3-utils
.trackBlock()
Returns the block information for any new block as soon as they are mined. It's the reactive equivalent to web3.eth.getBlock("latest")
.
subspace.trackBlock().subscribe(block => console.log(block));
trackBlockNumber()
Returns the latest block number. It's the reactive equivalent to web3.eth.getBlockNumber
.
subspace.trackBlockNumber().subscribe(blockNumber => console.log(blockNumber));
trackGasPrice()
Returns the current gas price oracle. It's the reactive equivalent to web3.eth.getGasPrice
.
subspace.trackGasPrice().subscribe(gasPrice => console.log(gasPrice));
trackAverageBlocktime()
Returns the moving average block time taking in account the latest 10 blocks. The time is returned in milliseconds:
subspace.trackAverageBlocktime().subscribe(blocktimeMS => console.log(blocktimeMS));
Generally for a library, webpack
is not used to produce the build in dist/. rather, but babel
and let downstream projects make the choice of webpack or rollup, etc. Subspace's node and browser builds were heavy and there was no hope a downstream developer's build tools can treeshake.
This minor release uses babel to build the sources into dist/
for nodejs, lib/
for the browser and module/
for es6. A DApp (or project, generally speaking) that makes use of Subspace will, with its own front-end tooling (e.g. webpack or rollup, in the context of create-react-app for example) be responsible for tree shaking and creating builds.
In addition to this change, dependency versions were bumped up.
Subspace is now under the @embarklabs Org. Versions under @status-im Org have been deprecated.
# Using npm
npm install --save @embarklabs/subspace
# Using yarn
yarn add @embarklabs/subspace
Previously Subspace only worked with WebSockets for event subscriptions, and polled for changes only if the callInterval
option was specified, displaying a warning indicating that the use of providers other than WebSocketProvider
was discouraged.
With this release, Subspace will identify if the provider supports subscriptions and use them automatically. If no subscriptions are available, it will assume the provider does not support them and poll the contract for new changes periodically.
Subscriptions can be disabled with the disableSubscriptions
option.
let subspace = new Subspace({disableSubscriptions: true})
An example DApp using GraphQL with Subspace can now be found at https://github.com/embark-framework/subspace/tree/master/examples/react-graphql-example1 .
from
address when instantiating a Web3 Contract with Subspace tracking functionality.track()
is added only to event names as they're specified in the ABI. Tracking events by signature is not allowedThe website and up to date documentation can be found at https://subspace.status.im
Added trackLogs()
to react to incoming logs
subspace.trackLogs({address: tokenAddress, topics: [eventTopic1, eventTopic2, null]}).subscribe((v) => {
console.log(v);
});
In cases where you don't want persistence, you can now disable it with the disableDatabase
option
let subspace = new Subspace({disableDatabase: true})
An example DApp using Vue with Subspace can now be found at https://github.com/embark-framework/subspace/tree/master/examples/vue
Subspace is a framework agnostic JS library that embraces reactive programming with RxJS, by observing asynchronous changes in Smart Contracts, and providing methods to track and subscribe to events, changes to the state of contracts and address balances, and react to these changes and events via observables.
Subspace also takes care of syncing under the hood, saving & loading the state in a local database.
npm install --save @status-im/subspace
You can track events and react to their values. With Subspace observables doing event sourcing is easy.
import { $average, $latest } from "@status-im/subspace";
const rating$ = Product.events.Rating.track().map("rating"));
rating$.pipe($latest(5), $average()).subscribe((rating) => {
console.log("average rating of the last 5 events is " + rating)
});
You can track changes to a contract state variable, by specifying the view function and arguments to call and query the contract
const productTitle$ = ProductList.methods.products(0).track().map("title");
productTitle$.subscribe((title) => console.log("product title is " + title));
You can also track changes in both ETH and ERC20 token balances
const address = "0x0001020304050607080900010203040506070809";
subspace.trackBalance(address).subscribe((balance) => {
console.log("ETH balance is ", balance)
});
subspace.trackBalance(address, "0x744d70fdbe2ba4cf95131626614a1763df805b9e").subscribe((balance) => {
console.log("SNT balance is ", balance)
});
note: Subspace is framework agnostic and can integrate with ANY framework.
Subspace can make any react component compatible with observables so you easily reactive components
import { observe } from "@status-im/subspace/react";
const ProductComponent = ({ maxRating, minRating, averageRating }) => {
return <ul>
<li><b>minimum rating: </b> {minRating}</li>
<li><b>maximum rating: </b> {maxRating}</li>
<li><b>average rating: </b> {averageRating}</li>
</ul>;
};
const ReactiveProductComponent = observe(ProductComponent);
const Product = subspace.contract({abi, address});
const rating$ = Product.events.Rating.track().map("rating").pipe(map(x => parseInt(x)));
ReactDOM.render(
<ReactiveProductComponent
maxRating={rating$.pipe($max())}
minRating={rating$.pipe($min())}
averageRating={rating$.pipe($average())}
/>,
document.getElementById('hello-example')
);