Apollo subscriptions - Nextjs - Error: Observable cancelled prematurely at Concast.removeObserver
Apollo subscriptions - Nextjs - Error: Observable cancelled prematurely at Concast.removeObserver
我正在尝试在我的 nextjs 项目中使用 apollo/graphql 订阅,我的 graphql 服务器放置在外部 nextjs 服务中,我可以毫无问题地处理查询和突变,但是当我使用 useSubscription 的实现时,我出现以下错误:
"Error: Observable cancelled prematurely
at Concast.removeObserver (webpack-internal:///../../node_modules/@apollo/client/utilities/observables/Concast.js:118:33)
at eval (webpack-internal:///../../node_modules/@apollo/client/utilities/observables/Concast.js:21:47)
at cleanupSubscription (webpack-internal:///../../node_modules/zen-observable-ts/module.js:92:7)
at Subscription.unsubscribe (webpack-internal:///../../node_modules/zen-observable-ts/module.js:207:7)
at cleanupSubscription (webpack-internal:///../../node_modules/zen-observable-ts/module.js:97:21)
at Subscription.unsubscribe (webpack-internal:///../../node_modules/zen-observable-ts/module.js:207:7)
at eval (webpack-internal:///../../node_modules/@apollo/client/react/hooks/useSubscription.js:106:26)
at safelyCallDestroy (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:22763:5)
at commitHookEffectListUnmount (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:22927:11)
at invokePassiveEffectUnmountInDEV (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:24998:13)
at invokeEffectsInDev (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:27137:11)
at commitDoubleInvokeEffectsInDEV (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:27110:7)
at flushPassiveEffectsImpl (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:26860:5)
at flushPassiveEffects (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:26796:14)
at eval (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:26592:9)
at workLoop (webpack-internal:///../../node_modules/scheduler/cjs/scheduler.development.js:266:34)
at flushWork (webpack-internal:///../../node_modules/scheduler/cjs/scheduler.development.js:239:14)
at MessagePort.performWorkUntilDeadline (webpack-internal:///../../node_modules/scheduler/cjs/scheduler.development.js:533:21)"
我知道订阅服务器工作正常,因为我可以从 apollo studio 收听我已经用 create-react-app 创建了一个 spa,它工作正常
我用过:
服务器:
- "apollo-server-express": "^3.6.7"
- "graphql-ws": "^5.7.0"
客户端
- “下一个”:“^12.1.5”
- "@apollo/client": "^3.5.10"
- "graphql-ws": "^5.7.0"
挂钩实现
const room = useSubscription(
gql`
subscription onRoomAdded($roomAddedId: ID!) {
roomAdded(id: $roomAddedId) {
id
name
}
}
`
);
客户端实现
import { ApolloClient, HttpLink, InMemoryCache, split } from '@apollo/client';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { getMainDefinition } from '@apollo/client/utilities';
import { createClient } from 'graphql-ws';
import fetch from 'isomorphic-fetch';
const HOST = 'http://localhost:3001/graphql';
const HOST_WS = 'ws://localhost:3001/graphql';
const isServer = typeof window === 'undefined';
if (isServer) {
global.fetch = fetch;
}
const httpLink = new HttpLink({
uri: HOST,
});
const link = isServer
? httpLink
: split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
new GraphQLWsLink(
createClient({
url: HOST_WS,
})
),
httpLink
);
const client = new ApolloClient({
ssrMode: isServer,
link,
cache: new InMemoryCache(),
});
export default client;
对这个问题有什么想法吗?我认为问题可能是 NextJS 仅适用于 subscriptions-transport-ws 但在官方 apollo 文档中表明新的官方方式是使用 graphql-ws 另一个库已经无人维护
更新!
我已经检查过订阅在生产构建中是否正常工作,我正在研究如何在开发过程中实施。欢迎提出任何建议。
如果它在生产环境中工作,但在开发环境中不工作,您可能会遇到与我的 React SPA 相同的问题:StrictMode 和双渲染 described in this github issue。
到目前为止,我已经找到了 2 种方法来实现它:
- 移除
StrictMode
- 订阅香草 JS 而不是
useSubscription
const ON_USER_ADDED = gql`
subscription OnUserAdded {
userAdded {
name
id
}
}
`;
const subscribe = () => {
client.subscribe({
query: ON_USER_ADDED,
}).subscribe({
next(data) {
console.log('data', data);
},
complete(){
console.log('complete');
},
error(err) {
console.log('error', err);
}
})
};
我正在尝试在我的 nextjs 项目中使用 apollo/graphql 订阅,我的 graphql 服务器放置在外部 nextjs 服务中,我可以毫无问题地处理查询和突变,但是当我使用 useSubscription 的实现时,我出现以下错误:
"Error: Observable cancelled prematurely at Concast.removeObserver (webpack-internal:///../../node_modules/@apollo/client/utilities/observables/Concast.js:118:33) at eval (webpack-internal:///../../node_modules/@apollo/client/utilities/observables/Concast.js:21:47) at cleanupSubscription (webpack-internal:///../../node_modules/zen-observable-ts/module.js:92:7) at Subscription.unsubscribe (webpack-internal:///../../node_modules/zen-observable-ts/module.js:207:7) at cleanupSubscription (webpack-internal:///../../node_modules/zen-observable-ts/module.js:97:21) at Subscription.unsubscribe (webpack-internal:///../../node_modules/zen-observable-ts/module.js:207:7) at eval (webpack-internal:///../../node_modules/@apollo/client/react/hooks/useSubscription.js:106:26) at safelyCallDestroy (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:22763:5) at commitHookEffectListUnmount (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:22927:11) at invokePassiveEffectUnmountInDEV (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:24998:13) at invokeEffectsInDev (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:27137:11) at commitDoubleInvokeEffectsInDEV (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:27110:7) at flushPassiveEffectsImpl (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:26860:5) at flushPassiveEffects (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:26796:14) at eval (webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:26592:9) at workLoop (webpack-internal:///../../node_modules/scheduler/cjs/scheduler.development.js:266:34) at flushWork (webpack-internal:///../../node_modules/scheduler/cjs/scheduler.development.js:239:14) at MessagePort.performWorkUntilDeadline (webpack-internal:///../../node_modules/scheduler/cjs/scheduler.development.js:533:21)"
我知道订阅服务器工作正常,因为我可以从 apollo studio 收听我已经用 create-react-app 创建了一个 spa,它工作正常
我用过:
服务器:
- "apollo-server-express": "^3.6.7"
- "graphql-ws": "^5.7.0"
客户端
- “下一个”:“^12.1.5”
- "@apollo/client": "^3.5.10"
- "graphql-ws": "^5.7.0"
挂钩实现
const room = useSubscription(
gql`
subscription onRoomAdded($roomAddedId: ID!) {
roomAdded(id: $roomAddedId) {
id
name
}
}
`
);
客户端实现
import { ApolloClient, HttpLink, InMemoryCache, split } from '@apollo/client';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { getMainDefinition } from '@apollo/client/utilities';
import { createClient } from 'graphql-ws';
import fetch from 'isomorphic-fetch';
const HOST = 'http://localhost:3001/graphql';
const HOST_WS = 'ws://localhost:3001/graphql';
const isServer = typeof window === 'undefined';
if (isServer) {
global.fetch = fetch;
}
const httpLink = new HttpLink({
uri: HOST,
});
const link = isServer
? httpLink
: split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
new GraphQLWsLink(
createClient({
url: HOST_WS,
})
),
httpLink
);
const client = new ApolloClient({
ssrMode: isServer,
link,
cache: new InMemoryCache(),
});
export default client;
对这个问题有什么想法吗?我认为问题可能是 NextJS 仅适用于 subscriptions-transport-ws 但在官方 apollo 文档中表明新的官方方式是使用 graphql-ws 另一个库已经无人维护
更新! 我已经检查过订阅在生产构建中是否正常工作,我正在研究如何在开发过程中实施。欢迎提出任何建议。
如果它在生产环境中工作,但在开发环境中不工作,您可能会遇到与我的 React SPA 相同的问题:StrictMode 和双渲染 described in this github issue。 到目前为止,我已经找到了 2 种方法来实现它:
- 移除
StrictMode
- 订阅香草 JS 而不是
useSubscription
const ON_USER_ADDED = gql`
subscription OnUserAdded {
userAdded {
name
id
}
}
`;
const subscribe = () => {
client.subscribe({
query: ON_USER_ADDED,
}).subscribe({
next(data) {
console.log('data', data);
},
complete(){
console.log('complete');
},
error(err) {
console.log('error', err);
}
})
};