import 'regenerator-runtime/runtime'; import React, { Component } from 'react'; import { parse } from 'graphql/language/parser'; import { API } from 'aws-amplify'; const getOperationType = operation => { const doc = parse(operation); const { definitions: [{ operation: operationType }], } = doc; return operationType; }; interface IConnectProps { mutation?: any; onSubscriptionMsg?: (prevData: any, data?: any) => any; query?: any; subscription?: any; } interface IConnectState { loading: boolean; data: any; errors: any[]; mutation: any; } export default class Connect extends Component { subSubscription: any; constructor(props: IConnectProps) { super(props); this.state = this.getInitialState(); this.subSubscription = null; } getInitialState() { const { query } = this.props; return { loading: query && !!query.query, data: {}, errors: [], mutation: () => console.warn('Not implemented'), }; } getDefaultState(): IConnectState { return { loading: false, data: {}, errors: [], mutation: () => console.warn('Not implemented'), }; } async _fetchData() { this._unsubscribe(); this.setState({ loading: true }); const { query: { query, variables = {} } = {}, mutation: { query: mutation, mutationVariables = {} } = {}, subscription, onSubscriptionMsg = prevData => prevData, } = this.props; let { data, mutation: mutationProp, errors } = this.getDefaultState(); const hasValidQuery = query && getOperationType(query) === 'query'; const hasValidMutation = mutation && getOperationType(mutation) === 'mutation'; const hasValidSubscription = subscription && getOperationType(subscription.query) === 'subscription'; if (!hasValidQuery && !hasValidMutation && !hasValidSubscription) { console.warn('No query, mutation or subscription was specified'); } if (hasValidQuery) { try { data = null; const response = await API.graphql({ query, variables, }); // @ts-ignore data = response.data; } catch (err) { data = err.data; errors = err.errors; } } if (hasValidMutation) { mutationProp = async variables => { const result = await API.graphql({ query: mutation, variables, }); this.forceUpdate(); return result; }; } if (hasValidSubscription) { const { query: subsQuery, variables: subsVars } = subscription; try { const observable = API.graphql({ query: subsQuery, variables: subsVars, }); // @ts-ignore this.subSubscription = observable.subscribe({ next: ({ value: { data } }) => { const { data: prevData } = this.state; const newData = onSubscriptionMsg(prevData, data); this.setState({ data: newData }); }, error: err => console.error(err), }); } catch (err) { errors = err.errors; } } this.setState({ data, errors, mutation: mutationProp, loading: false, }); } _unsubscribe() { if (this.subSubscription) { this.subSubscription.unsubscribe(); } } async componentDidMount() { this._fetchData(); } componentWillUnmount() { this._unsubscribe(); } componentDidUpdate(prevProps: IConnectProps) { const { loading } = this.state; const { query: newQueryObj, mutation: newMutationObj } = this.props; const { query: prevQueryObj, mutation: prevMutationObj } = prevProps; // query const { query: newQuery, variables: newQueryVariables } = newQueryObj || {}; const { query: prevQuery, variables: prevQueryVariables } = prevQueryObj || {}; const queryChanged = prevQuery !== newQuery || JSON.stringify(prevQueryVariables) !== JSON.stringify(newQueryVariables); // mutation const { query: newMutation, variables: newMutationVariables } = newMutationObj || {}; const { query: prevMutation, variables: prevMutationVariables } = prevMutationObj || {}; const mutationChanged = prevMutation !== newMutation || JSON.stringify(prevMutationVariables) !== JSON.stringify(newMutationVariables); if (!loading && (queryChanged || mutationChanged)) { this._fetchData(); } } render() { const { data, loading, mutation, errors } = this.state; return ( // @ts-ignore this.props.children({ data, errors, loading, mutation, }) || null ); } }