AWS Amplify react Connect组件中的多个订阅

时间:2019-03-23 09:01:40

标签: reactjs aws-amplify

我有一个待办事项列表,当从列表中添加或删除项目时,我希望收到通知。

到目前为止,我已经实施了商品添加通知:

<Connect
        query={graphqlOperation(listTodos)}
        subscription={graphqlOperation(onCreateTodo)}
        onSubscriptionMsg={(prev, { onCreateTodo }) => {
            return addItem(prev, onCreateTodo)
        }}
    >
        {({ data: { listTodos }, loading, error }) => {
            if (loading) return "Loading"
            if (error) return "Error"

            return listTodos.items
                .map(({ id, name }) => <div key={id}>{name}</div>)
        }}
</Connect>

现在我想知道如何向该组件添加项目删除通知?订阅属性是否接受一个graphql操作数组?

谢谢!

3 个答案:

答案 0 :(得分:0)

您可以在组件中使用多个Connect组件。

<Connect query={graphqlOperation(listTodos)}>
  {({ data: { listTodos }, loading, error }) => {
    if (loading) return "Loading"
    if (error) return "Error"

    return listTodos.items.map(({ id, name }) => <div key={id}>{name}</div>)
  }}
</Connect>

<Connect
  subscription={graphqlOperation(subscriptions.onCreateTodo)}
  onSubscriptionMsg={(prev, { onCreateTodo }) => {
    // Do something
    return prev;
  }}
>
  {() => {}}
</Connect>

<Connect
  subscription={graphqlOperation(subscriptions.onUpdateTodo)}
  onSubscriptionMsg={(prev, { onUpdateTodo }) => {
    // Do something
    return prev;
  }}
>
  {() => {}}
</Connect>

答案 1 :(得分:0)

看起来当前的解决方案是按照github问题https://github.com/aws-amplify/amplify-js/issues/4813#issuecomment-582106596

中所述创建自己的Connect组件实现。

答案 2 :(得分:0)

我尝试了上面的Connect.ts版本,并在此线程中收到其他人报告的相同错误。因此,我创建了一个版本,您可以按照原始版本将多个订阅作为数组传递-您仍然可以传递单个订阅。 注意:此版本仅需要一个查询和一个onSubscriptionMessage -但是,您的onSubscriptionMessage可以是包装函数,用于检查传递给它的newData并根据此数据调用适当的更新,如下所示:

const onSubscriptionMessage = (prevQuery, newData) => {
    if(newData && newData.onDeleteItem) {
      return onRemoveItem(prevQuery, newData);
    }
    if(newData && newData.onCreateItem) {
      return onAddItem(prevQuery, newData);
    }
  };

针对多个订阅的Connect.ts,它具有一个查询和一个onSubscriptionMessage处理程序,该处理程序根据newData切换处理。

import * as React from 'react';
import { API, GraphQLResult } from '@aws-amplify/api';
import Observable from 'zen-observable-ts';

export interface IConnectProps {
    mutation?: any;
    onSubscriptionMsg?: (prevData: any) => any;
    query?: any;
    subscription?: any;
}

export interface IConnectState {
    loading: boolean;
    data: any;
    errors: any;
    mutation: any;
}

export class Connect extends React.Component<IConnectProps, IConnectState> {
    public subSubscriptions: Array<Promise<GraphQLResult<object>> | Observable<object>>;
    private mounted: boolean = false;

    constructor(props:any) {
        super(props);

        this.state = this.getInitialState();
        this.subSubscriptions = [];
    }

    getInitialState() {
        const { query } = this.props;
        return {
            loading: query && !!query.query,
            data: {},
            errors: [],
            mutation: () => console.warn('Not implemented'),
        };
    }

    getDefaultState() {
        return {
            loading: false,
            data: {},
            errors: [],
            mutation: () => console.warn('Not implemented'),
        };
    }

    async _fetchData() {
        this._unsubscribe();
        this.setState({ loading: true });

        const {
            // @ts-ignore
            query: { query, variables = {} } = {},
            //@ts-ignore
            mutation: { query: mutation,
                //eslint-disable-next-line
                mutationVariables = {} } = {},
            subscription,
            onSubscriptionMsg = (prevData:any) => prevData,
        } = this.props;

        let { data, mutation: mutationProp, errors } = this.getDefaultState();

        if (
            !API ||
            typeof API.graphql !== 'function' ||
            typeof API.getGraphqlOperationType !== 'function'
        ) {
            throw new Error(
                'No API module found, please ensure @aws-amplify/api is imported'
            );
        }

        const hasValidQuery =
            query && API.getGraphqlOperationType(query) === 'query';
        const hasValidMutation =
            mutation && API.getGraphqlOperationType(mutation) === 'mutation';

        const validSubscription = (subscription:any) => subscription
            && subscription.query
            && API.getGraphqlOperationType(subscription.query) === 'subscription';

        const validateSubscriptions = (subscription:any) => {
            let valid = false;
            if(Array.isArray(subscription)) {
               valid = subscription.map(validSubscription).indexOf(false) === -1;
            } else {
               valid = validSubscription(subscription)
            }
            return valid;
        };

        const hasValidSubscriptions = validateSubscriptions(subscription);

        if (!hasValidQuery && !hasValidMutation && !hasValidSubscriptions) {
            console.warn('No query, mutation or subscription was specified correctly');
        }

        if (hasValidQuery) {
            try {
                // @ts-ignore
                data = null;

                const response = await API.graphql({ query, variables });

                // @ts-ignore
                data = response.data;
            } catch (err) {
                data = err.data;
                errors = err.errors;
            }
        }

        if (hasValidMutation) {
            // @ts-ignore
            mutationProp = async variables => {
                const result = await API.graphql({ query: mutation, variables });

                this.forceUpdate();
                return result;
            };
        }

        if (hasValidSubscriptions) {

            // @ts-ignore
            const connectSubscriptionToOnSubscriptionMessage = (subscription) => {

                // @ts-ignore
                const {query: subsQuery, variables: subsVars} = subscription;

                try {
                    const observable = API.graphql({
                        query: subsQuery,
                        variables: subsVars,
                    });

                    // @ts-ignore
                    this.subSubscriptions.push(observable.subscribe({
                        // @ts-ignore
                        next: ({value: {data}}) => {
                            const {data: prevData} = this.state;
                            // @ts-ignore
                            const newData = onSubscriptionMsg(prevData, data);
                            if (this.mounted) {
                                this.setState({data: newData});
                            }
                        },
                        error: (err:any) => console.error(err),
                    }));

                } catch (err) {
                    errors = err.errors;
                }
            };
            if(Array.isArray(subscription)) {
                subscription.forEach(connectSubscriptionToOnSubscriptionMessage);
            } else {
                connectSubscriptionToOnSubscriptionMessage(subscription)
            }
        }

        this.setState({ data, errors, mutation: mutationProp, loading: false });
    }

    _unsubscribe() {
        const __unsubscribe = (subSubscription:any) => {
            if (subSubscription) {
                subSubscription.unsubscribe();
            }
        };
        this.subSubscriptions.forEach(__unsubscribe);
    }

    async componentDidMount() {
        this._fetchData();
        this.mounted = true;
    }

    componentWillUnmount() {
        this._unsubscribe();
        this.mounted = false;
    }

    componentDidUpdate(prevProps:any) {
        const { loading } = this.state;

        const { query: newQueryObj, mutation: newMutationObj, subscription: newSubscription} = this.props;
        const { query: prevQueryObj, mutation: prevMutationObj,  subscription: prevSubscription } = prevProps;

        // query
        // @ts-ignore
        const { query: newQuery, variables: newQueryVariables } = newQueryObj || {};
        // @ts-ignore
        const { query: prevQuery, variables: prevQueryVariables } =
        prevQueryObj || {};
        const queryChanged =
            prevQuery !== newQuery ||
            JSON.stringify(prevQueryVariables) !== JSON.stringify(newQueryVariables);

        // mutation
        // @ts-ignore
        const { query: newMutation, variables: newMutationVariables } =
        newMutationObj || {};
        // @ts-ignore
        const { query: prevMutation, variables: prevMutationVariables } =
        prevMutationObj || {};
        const mutationChanged =
            prevMutation !== newMutation ||
            JSON.stringify(prevMutationVariables) !==
            JSON.stringify(newMutationVariables);

        // subscription
        // @ts-ignore
        const { query: newSubsQuery, variables: newSubsVars } = newSubscription || {};
        // @ts-ignore
        const { query: prevSubsQuery, variables: prevSubsVars } = prevSubscription || {};
        const subscriptionChanged =
            prevSubsQuery !== newSubsQuery  ||
            JSON.stringify(prevSubsVars) !==
            JSON.stringify(newSubsVars);

        if (!loading && (queryChanged || mutationChanged || subscriptionChanged)) {
            this._fetchData();
        }
    }

    render() {
        const { data, loading, mutation, errors } = this.state;
        // @ts-ignore
        return this.props.children({ data, errors, loading, mutation }) || null;
    }
}

/**
 * @deprecated use named import
 */
export default Connect;

用法:本文顶部是onSubscriptionMessage的示例。

<Connect
     query={graphqlOperation(listTopics)}
     subscription={[graphqlOperation(onCreateTopic),  graphqlOperation(onDeleteTopic)]}
     onSubscriptionMsg={onSubscriptionMessage}>
{.....}
</Connect>