我尝试了上面的 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>