2
votes

Abonnements multiples dans le composant AWS Amplify React Connect

J'ai une liste d'éléments à faire et je souhaite recevoir des notifications lorsque des éléments sont ajoutés ou supprimés de la liste.

Jusqu'à présent, j'ai implémenté la notification d'ajout d'élément:

<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>

Maintenant, je me demande comment puis-je ajouter une notification de suppression d'élément à ce composant? L'attribut d'abonnement accepte-t-il un tableau d'opérations graphql?

Merci!


0 commentaires

3 Réponses :


0
votes

Vous pouvez utiliser plusieurs composants Connect dans votre composant.

<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 commentaires

Cela ne fonctionne pas car la variable 'prev' ne contient aucune information dans le second composant <Connect>.



0
votes

Il semble que la solution actuelle consiste à créer votre propre implémentation du composant Connect comme décrit dans ce problème github: https://github.com/aws-amplify/amplify-js/issues/4813#issuecomment-582106596


0 commentaires

0
votes

J'ai essayé la version Connect.ts ci-dessus et j'ai obtenu les mêmes erreurs signalées par d'autres dans ce fil. J'ai donc créé une version dans laquelle vous pouvez transmettre plusieurs abonnements en tant que tableau - vous pouvez toujours transmettre un seul abonnement également - selon la version originale. Remarque: cette version ne prend qu'une seule requête et un seul onSubscriptionMessage - cependant, votre onSubscriptionMessage peut être une fonction wrapper qui examine le newData qui lui est passé et appelle la mise à jour appropriée en fonction de ces données comme ceci:

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

Connect.ts pour plusieurs abonnements avec une seule requête et un seul gestionnaire onSubscriptionMessage qui bascule la gestion en fonction de 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;

Utilisation: un exemple de onSubscriptionMessage est en haut de cet article.

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


0 commentaires