You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apollo GraphQL React Client订阅异常:仅发送connection_init未完成订阅

GraphQL订阅未发送subscribe消息的修复方案

问题场景

尝试用GraphQL Apollo Client实现订阅Demo,已有可用的GraphQL API,但WebSocket连接仅发送connection_init消息,未触发subscribe,订阅无法正常工作。现有代码如下:

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import { ApolloClient, InMemoryCache, ApolloProvider, gql, useQuery } from '@apollo/client';
import { split, HttpLink } from '@apollo/client';
import { getMainDefinition } from '@apollo/client/utilities';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { createClient } from 'graphql-ws';
import { useSubscription } from '@apollo/react-hooks'
import reportWebVitals from './reportWebVitals';


const httpLink = new HttpLink({
  uri: 'https://mygraphql.api'
});

const wsLink = new GraphQLWsLink(createClient({
  url: 'wss://mygraphql.api',
  options: {
    reconnect: true
  }
}));

const splitLink = split(
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    );
  },
  wsLink,
  httpLink,
);

const client = new ApolloClient({
  link: splitLink,
  cache: new InMemoryCache(),
  fetchOptions: {
    mode: 'no-cors',
  }
});

const FAMILIES_SUBSCRIPTION = gql`
  subscription{
    onFamilyCreated {
      id
      name
    }
  }
`;

function LastFamily() {
  const { loading, error, data } = useSubscription(FAMILIES_SUBSCRIPTION, {
    variables: { },
    onData: data => console.log('new data', data)
  });

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error!</div>;
  console.log(data);
  const family = data.onFamilyCreated[0];

  return (
    <div>
      <h1>{family.name}</h1>
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  (<ApolloProvider client={client}>
   <div>
   <LastFamily />
    </div>
  </ApolloProvider>));
reportWebVitals();

问题原因及修复步骤

1. 修正useSubscription的导入路径

Apollo Client 3.x及以上版本,useSubscription钩子已经整合到@apollo/client包中,无需再从@apollo/react-hooks导入,混用会导致版本兼容问题,直接影响订阅触发逻辑。

修改导入:

// 删除旧导入
import { useSubscription } from '@apollo/react-hooks'
// 改为从@apollo/client统一导入
import { ApolloClient, InMemoryCache, ApolloProvider, gql, useQuery, useSubscription } from '@apollo/client';

2. 调整WebSocket客户端配置结构

graphql-ws的createClient参数中,reconnect是顶层配置项,不需要嵌套在options里,旧写法会导致配置不生效,影响连接稳定性。

修改wsLink配置:

const wsLink = new GraphQLWsLink(createClient({
  url: 'wss://mygraphql.api',
  reconnect: true // 直接放在顶层,移除options嵌套
}));

3. 修正订阅数据的处理逻辑

订阅onFamilyCreated返回的是单个新创建的Family对象,而非数组,取data.onFamilyCreated[0]会直接报错。同时,订阅是持续接收数据的,初始loading状态过后,新数据会触发组件重渲染,需要添加兜底处理避免空值报错。

修改LastFamily组件:

function LastFamily() {
  const { loading, error, data } = useSubscription(FAMILIES_SUBSCRIPTION, {
    onData: ({ data: newData }) => console.log('new data', newData)
  });

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  // 订阅未收到数据时的兜底展示
  if (!data?.onFamilyCreated) return <div>No family created yet</div>;
  
  const family = data.onFamilyCreated;

  return (
    <div>
      <h1>Latest Family: {family.name}</h1>
      <p>ID: {family.id}</p>
    </div>
  );
}

4. 移除no-cors配置

fetchOptions.mode: 'no-cors'会限制浏览器处理跨域请求的能力,WebSocket连接可能因此无法正常完成握手,除非API明确要求该模式,否则建议直接移除。

修改ApolloClient配置:

const client = new ApolloClient({
  link: splitLink,
  cache: new InMemoryCache(),
  // 移除fetchOptions配置
});

修改后的完整代码

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import { ApolloClient, InMemoryCache, ApolloProvider, gql, useQuery, useSubscription } from '@apollo/client';
import { split, HttpLink } from '@apollo/client';
import { getMainDefinition } from '@apollo/client/utilities';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { createClient } from 'graphql-ws';
import reportWebVitals from './reportWebVitals';


const httpLink = new HttpLink({
  uri: 'https://mygraphql.api'
});

const wsLink = new GraphQLWsLink(createClient({
  url: 'wss://mygraphql.api',
  reconnect: true
}));

const splitLink = split(
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    );
  },
  wsLink,
  httpLink,
);

const client = new ApolloClient({
  link: splitLink,
  cache: new InMemoryCache(),
});

const FAMILIES_SUBSCRIPTION = gql`
  subscription{
    onFamilyCreated {
      id
      name
    }
  }
`;

function LastFamily() {
  const { loading, error, data } = useSubscription(FAMILIES_SUBSCRIPTION, {
    onData: ({ data: newData }) => console.log('new data', newData)
  });

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  if (!data?.onFamilyCreated) return <div>No family created yet</div>;
  
  const family = data.onFamilyCreated;

  return (
    <div>
      <h1>Latest Family: {family.name}</h1>
      <p>ID: {family.id}</p>
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <ApolloProvider client={client}>
    <div>
      <LastFamily />
    </div>
  </ApolloProvider>
);
reportWebVitals();

完成以上修改后,WebSocket连接会正常发送connection_init和subscribe消息,订阅成功后就能实时接收新创建的Family数据并渲染到页面上。

内容的提问来源于stack exchange,提问作者shinjidev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 13:55:20