Closed mwaeckerlin closed 5 years ago
Here is my suggestion, as I implemented it in my project. If you want, @honestbonsai, you may integrate my solution in drizzle-react-components
(or I can do it for you, including tests and docu):
DynamicContracts.js
import React from "react";
import PropTypes from "prop-types";
import { drizzleConnect } from "drizzle-react";
import { ContractData } from "drizzle-react-components";
class DynamicContract extends React.Component {
constructor(props, context) {
super(props);
var contractConfig = {
contractName: this.props.contract || this.props.address,
web3Contract: new context.drizzle.web3.eth.Contract(
this.props.abi,
this.props.address,
this.props.options
)
};
context.drizzle.addContract(contractConfig, this.props.events);
}
render() {
try {
const contracts = this.props.store.getState().contracts;
// Contract is not yet intialized.
if (
!contracts[this.props.contract || this.props.address] ||
!contracts[this.props.contract || this.props.address].initialized
) {
return <span>Initializing...</span>;
}
if (this.props.render)
if (this.props.method)
return (
<ContractData
contracts={contracts}
contract={this.props.contract || this.props.address}
method={this.props.method}
methodArgs={this.props.methodArgs}
sendArgs={this.props.sendArgs}
render={this.props.render}
/>
);
else
return this.props.render(this.props.contracts, this.props.contract || this.props.address);
if (this.props.children) {
const extraProps = {
contracts: contracts,
contract: this.props.contract || this.props.address
};
const mapper = function(child) {
if (!React.isValidElement(child)) return child;
return React.cloneElement(child, {
...extraProps,
children: React.Children.map(child.props.children, mapper)
});
};
return React.Children.map(this.props.children, mapper);
}
if (this.props.method)
return (
<ContractData
contracts={contracts}
contract={this.props.contract || this.props.address}
method={this.props.method}
methodArgs={this.props.methodArgs}
sendArgs={this.props.sendArgs}
/>
);
return <>Contract: {this.props.contract || this.props.address}</>;
} catch (e) {
console.log("ERROR in DynamicContract render", e);
return <div class="error">ERROR: {e.message}</div>;
}
}
}
DynamicContract.contextTypes = {
drizzle: PropTypes.object
};
DynamicContract.propTypes = {
contracts: PropTypes.object.isRequired,
address: PropTypes.string.isRequired,
abi: PropTypes.array.isRequired,
contract: PropTypes.string,
options: PropTypes.object,
events: PropTypes.object,
render: PropTypes.func
};
/*
* Export connected component.
*/
const mapStateToProps = state => {
return {
contracts: state.contracts
};
};
export default drizzleConnect(DynamicContract, mapStateToProps);
For the usage you have several options:
ContractData
or ContractForm
(where the contract prop is set automatically by DynamicContract
)method
property, then this method is calledExample:
<DynamicContracs address="0x…" abi={abi}>
X=<ContracData method="getX" />
</DynamicContracs>
@mwaeckerlin Does this update when the props change? E.g. I pass in new props for address
etc. I would expect the component to make a new dynamic contract. But I don't see anything to handle that. Correct me if I'm misunderstanding.
Right now it looks like it only works for 1 contract on initialization. New props should always update the component.
@honestbonsai, watching props could be added the same way, as you do it e.g. in ContractData
.
Yes, by design, you instanciate one dynamic contract per address.
I use it in the following context: A method on a contract, accessed using ContractData
, returns a list of addresses of other contracts. In the render
props, I iterate through this list do show information of the referenced contracts:
<ContractData
contract="Parent"
method="getChildren"
render={children => (
children.map(child => (
<ul>
<DynamicContract
address={child}
abi={childAbi}>
<li key={child}>
<ContractData method="getSomeText" /> // contract is set by DynamicContract
</li>
</DynamicContract>
</ul>
))
)}
/>
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Are you interested to get it?
It works fine in our project …
@mwaeckerlin Sorry for the delay.
There's a few things I think need to be addressed:
context.drizzle.addContract
should happen every time props change. Of course there would have to be a way to check we aren't adding contracts which have already been added once.render props vs children: The rest of the components in DRC use render props for custom rendering, so I think this one should align with that as well.
In my opinion, the code for render props is more straight forward than the children version since we don't have to use lower level React apis, can just use simple components.
Thoughts?
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
This issue has been closed, but can be re-opened if further comments indicate that the problem persists. Feel free to tag maintainers if there is no reply to further comments.
Render contracs that are dynamically instanciated using an address and an ABI, i.e. render the target contract in an array of addresses to contracts. Perhaps this will need a new class, sich as
DynamicContract.js
.