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,它工作正常

我用过:

服务器:

客户端

挂钩实现

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 种方法来实现它:

  1. 移除StrictMode
  2. 订阅香草 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);
    }
  })
};