import React from 'react'; import { Result } from 'mycrypto-nano-result'; import { shepherdProvider } from 'libs/nodes'; import ERC20 from 'libs/erc20'; import Spinner from 'components/ui/Spinner'; import { Input } from 'components/ui'; interface OwnProps { fieldToFetch: keyof Pick; fieldName: string; address?: string; userInputValidator(input: string): Result; fetchedFieldValidator?(input: any): Result; shouldEnableAutoField(input: Result): boolean; onChange(symbol: Result): void; } interface State { field: Result; autoField: boolean; userInput: string; addressToLoad?: string; loading: boolean; } export class FieldInput extends React.Component { public static getDerivedStateFromProps( nextProps: OwnProps, prevState: State ): Partial | null { if (nextProps.address && nextProps.address !== prevState.addressToLoad) { return { loading: true, autoField: true, addressToLoad: nextProps.address }; } return null; } public state: State = { userInput: '', autoField: true, field: Result.from({ res: '' }), loading: false }; private currentRequest: Promise | null; public componentDidUpdate() { if (this.state.addressToLoad && this.state.loading) { this.attemptToLoadField(this.state.addressToLoad); } } public componentWillUnmount() { if (this.currentRequest) { this.currentRequest = null; } } public render() { const { userInput, field, autoField, loading } = this.state; return ( ); } private handleFieldChange = (args: React.FormEvent) => { const userInput = args.currentTarget.value; const field = this.props.userInputValidator(userInput); this.setState({ userInput, field }); this.props.onChange(field); }; private attemptToLoadField(address: string) { // process request this.currentRequest = this.loadField(address) // set state on successful request e.g it was not cancelled // and then also set our current request to null .then(({ [this.props.fieldToFetch]: field }) => this.setState({ field, loading: false, autoField: this.props.shouldEnableAutoField(field) }) ) .catch(e => { console.error(e); // if the component is unmounted, then dont call set state if (!this.currentRequest) { return; } // otherwise it was a failed fetch call this.setState({ autoField: false, loading: false }); }) .then(() => (this.currentRequest = null)); } private loadField(address: string) { const { fieldToFetch } = this.props; return shepherdProvider .sendCallRequest({ data: ERC20[fieldToFetch].encodeInput(), to: address }) .then(ERC20[fieldToFetch].decodeOutput as any) .then(({ [fieldToFetch]: field }) => { let result: Result; if (this.props.fetchedFieldValidator) { result = this.props.fetchedFieldValidator(field); } else { result = Result.from({ res: field }); } // // this.props.onChange(result); return { [fieldToFetch]: result }; }); } }