fix: unsubscribe on prop change (#32)
* fix: unsubscribe on prop change * fix: reactive-graphql example
This commit is contained in:
parent
454cfdecfd
commit
8cf6d5c619
|
@ -0,0 +1,23 @@
|
||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
|
@ -0,0 +1,34 @@
|
||||||
|
phoenix - reactive-graphql example
|
||||||
|
===
|
||||||
|
Simple application that shows how to use graphql queries with Phoenix observables
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
- `ganache-cli`
|
||||||
|
- `yarn` or `npm` installed.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
In the parent folder, link the package with `yarn` or `npm`
|
||||||
|
```
|
||||||
|
yarn link
|
||||||
|
```
|
||||||
|
Then in the current folder link `phoenix`, and install the packages
|
||||||
|
```
|
||||||
|
yarn link phoenix
|
||||||
|
yarn
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
In a terminal execute
|
||||||
|
```
|
||||||
|
ganache-cli
|
||||||
|
```
|
||||||
|
|
||||||
|
In a different session, execute
|
||||||
|
```
|
||||||
|
node src/index.js
|
||||||
|
```
|
||||||
|
|
||||||
|
You'll see in the console the result of the graphql query
|
||||||
|
|
||||||
|
|
||||||
|
*Note*: this is a simple example application that does not include error handling for the web3 connection. Be sure `ganache-cli` is running in `localhost:8545` before executing the dapp.
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"name": "reactive-graphql-example",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"graphql": "^14.5.0",
|
||||||
|
"graphql-tag": "^2.10.1",
|
||||||
|
"graphql-tools": "^4.0.5",
|
||||||
|
"reactive-graphql": "^3.0.2",
|
||||||
|
"rxjs": "^6.5.2",
|
||||||
|
"web3": "^1.0.0-beta.37"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
const web3 = require('./web3');
|
||||||
|
|
||||||
|
const abi = [
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "myFunction",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "someValue",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "anotherValue",
|
||||||
|
"type": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "MyEvent",
|
||||||
|
"type": "event"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const data = "0x6080604052348015600f57600080fd5b5060f38061001e6000396000f3fe6080604052600436106039576000357c010000000000000000000000000000000000000000000000000000000090048063c3780a3a14603e575b600080fd5b348015604957600080fd5b5060506052565b005b60004342029050600081604051602001808281526020019150506040516020818303038152906040528051906020012090507fc3d6130248b5b68a864c047b2f68d895d420924130388d02d64b648005fe9ac78282604051808381526020018281526020019250505060405180910390a1505056fea165627a7a72305820613e35c5d1e8684ef5b31a7d993a139f1b5bbb409039d92db0fe78ed571d2ce20029";
|
||||||
|
|
||||||
|
const MyContract = new web3.eth.Contract(abi, {data, gas: "470000"});
|
||||||
|
|
||||||
|
MyContract.getInstance = async() => {
|
||||||
|
if(!web3.eth.defaultAccount){
|
||||||
|
const accounts = await web3.eth.getAccounts();
|
||||||
|
web3.eth.defaultAccount = accounts[0];
|
||||||
|
}
|
||||||
|
return MyContract.deploy().send({from: web3.eth.defaultAccount});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = MyContract;
|
|
@ -0,0 +1,129 @@
|
||||||
|
const Phoenix = require('phoenix');
|
||||||
|
const web3 = require('./web3');
|
||||||
|
const MyContract = require('./MyContract');
|
||||||
|
const { pluck } = require('rxjs/operators');
|
||||||
|
const { makeExecutableSchema } = require("graphql-tools");
|
||||||
|
const gql = require("graphql-tag");
|
||||||
|
const { graphql } = require("reactive-graphql");
|
||||||
|
|
||||||
|
|
||||||
|
const run = (async () => {
|
||||||
|
const eventSyncer = new Phoenix(web3.currentProvider);
|
||||||
|
await eventSyncer.init();
|
||||||
|
|
||||||
|
const MyContractInstance = await MyContract.getInstance();
|
||||||
|
|
||||||
|
// Creating some transactions
|
||||||
|
await MyContractInstance.methods.myFunction().send({from: web3.eth.defaultAccount});
|
||||||
|
await MyContractInstance.methods.myFunction().send({from: web3.eth.defaultAccount});
|
||||||
|
await MyContractInstance.methods.myFunction().send({from: web3.eth.defaultAccount});
|
||||||
|
await MyContractInstance.methods.myFunction().send({from: web3.eth.defaultAccount});
|
||||||
|
|
||||||
|
const typeDefs = `
|
||||||
|
type MyEvent {
|
||||||
|
someValue: Int
|
||||||
|
anotherValue: String
|
||||||
|
}
|
||||||
|
type Query {
|
||||||
|
myEvents: MyEvent!
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const resolvers = {
|
||||||
|
Query: {
|
||||||
|
myEvents: () => {
|
||||||
|
return eventSyncer.trackEvent(MyContractInstance, 'MyEvent', {filter: {}, fromBlock: 1})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const schema = makeExecutableSchema({ typeDefs, resolvers });
|
||||||
|
|
||||||
|
const query = gql`
|
||||||
|
query {
|
||||||
|
myEvents {
|
||||||
|
someValue
|
||||||
|
anotherValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const stream = graphql(schema, query).pipe(pluck('data', 'myEvents'));
|
||||||
|
stream.subscribe(x => {
|
||||||
|
console.log(x)
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
run();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async function deployContract() {
|
||||||
|
let accounts = await web3.eth.getAccounts();
|
||||||
|
|
||||||
|
// pragma solidity >=0.4.22 <0.6.0;
|
||||||
|
// contract Escrow {
|
||||||
|
// event Created(uint indexed escrowId, address buyer, address seller);
|
||||||
|
// function createEscrow(uint escrowId, address buyer, address seller) external {
|
||||||
|
// emit Created(escrowId, buyer, seller);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
let abi = [
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "escrowId",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "buyer",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "seller",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "createEscrow",
|
||||||
|
"outputs": [],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": true,
|
||||||
|
"name": "escrowId",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "buyer",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"name": "seller",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "Created",
|
||||||
|
"type": "event"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
var contract = new web3.eth.Contract(abi)
|
||||||
|
let instance = await contract.deploy({
|
||||||
|
data: '0x608060405234801561001057600080fd5b50610184806100206000396000f3fe60806040526004361061003b576000357c01000000000000000000000000000000000000000000000000000000009004806378015cf414610040575b600080fd5b34801561004c57600080fd5b506100b96004803603606081101561006357600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506100bb565b005b827fcbd6f84bfed2ee8cc01ea152b5d9f7126a72c410dbc5ab04c486a5800627b1908383604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a250505056fea165627a7a72305820cc868ec126578f5508ee248fb823cd9f1ac6deb0562091cdf31843840b2a56410029',
|
||||||
|
arguments: []
|
||||||
|
}).send({
|
||||||
|
from: accounts[0],
|
||||||
|
gas: '4700000'
|
||||||
|
})
|
||||||
|
return instance
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
const Web3 = require('web3');
|
||||||
|
const web3 = new Web3("ws://localhost:8545");
|
||||||
|
|
||||||
|
module.exports = web3;
|
File diff suppressed because it is too large
Load Diff
|
@ -32,4 +32,4 @@ node -r esm src/index.js
|
||||||
You'll see in the console how the state changes everytime phoenix receives an event.
|
You'll see in the console how the state changes everytime phoenix receives an event.
|
||||||
|
|
||||||
|
|
||||||
*Note*: this is a simple example application that does not include error handling for the web3 connection. Be sure `ganache-cli` is running in `localhost:8545` before browsing the dapp.
|
*Note*: this is a simple example application that does not include error handling for the web3 connection. Be sure `ganache-cli` is running in `localhost:8545` before executing the dapp.
|
|
@ -8,6 +8,14 @@ export function observe(WrappedComponent) {
|
||||||
subscriptions: {}
|
subscriptions: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsubscribe = prop => {
|
||||||
|
const subscriptions = {...this.state.subscriptions};
|
||||||
|
if(subscriptions[prop]) subscriptions[prop].unsubscribe();
|
||||||
|
delete subscriptions[prop];
|
||||||
|
|
||||||
|
this.setState({subscriptions});
|
||||||
|
}
|
||||||
|
|
||||||
subscribeToProp = prop => {
|
subscribeToProp = prop => {
|
||||||
if(!isObservable(this.props[prop])) return;
|
if(!isObservable(this.props[prop])) return;
|
||||||
|
|
||||||
|
@ -48,6 +56,9 @@ export function observe(WrappedComponent) {
|
||||||
Object.keys(prevProps).forEach(prop => {
|
Object.keys(prevProps).forEach(prop => {
|
||||||
if(!prevProps[prop] && this.props[prop]){
|
if(!prevProps[prop] && this.props[prop]){
|
||||||
this.subscribeToProp(prop);
|
this.subscribeToProp(prop);
|
||||||
|
} else if(prevProps[prop] !== this.props[prop]){
|
||||||
|
this.unsubscribe(prop);
|
||||||
|
this.subscribeToProp(prop);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue