Skip to content

useSubscription stop working in React v18 StrictMode #9664

@pobch

Description

@pobch

Intended outcome:

In React v18 StrictMode, data should be received from useSubscription().

Actual outcome:

data is always undefined even though the server actually sends it via websocket. Find more details in "How to reproduce the issue" below.

How to reproduce the issue:

  1. Use React v18
  2. Turn on StrictMode
  3. The complete code should look like this:
import React from 'react'
import ReactDOM from 'react-dom/client'
import {
  ApolloClient,
  InMemoryCache,
  ApolloProvider,
  HttpLink,
  split,
  useSubscription,
  gql,
} from '@apollo/client'
import { WebSocketLink } from '@apollo/client/link/ws'
import { SubscriptionClient } from 'subscriptions-transport-ws'
import { getMainDefinition } from '@apollo/client/utilities'

const INSTRUMENTS_SUBSCRIPTION = gql`
  subscription OnInstrumentsUpdated {
    instruments {
      ...
    }
  }
`

// ----------------------------- Component ---------------------------------
function App() {
  const { data, loading } = useSubscription(INSTRUMENTS_SUBSCRIPTION, {
    fetchPolicy: 'network-only',
  })

  if (loading) return <p>Loading...</p>

  return (
    <>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </>
  )
}

// ----------------------------- Setup Apollo ---------------------------------
const httpLink = new HttpLink({
  uri: 'http://localhost:8088/api/graphql',
})

const wsLink = new WebSocketLink(new SubscriptionClient('ws://localhost:8088/ws/graphql'))

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(),
})

// ----------------------------- React v18 StrictMode ---------------------------------
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
  <React.StrictMode>
    <ApolloProvider client={client}>
      <App />
    </ApolloProvider>
  </React.StrictMode>
)
  1. Start the app
  2. In App component, loading will be false, and data will always be undefined.
  3. In the network tab, it shows that Apollo immediately sends { type: "stop" } message right after { type: "start" }

Screen Shot 2565-05-03 at 18 18 01

If I remove <React.StrictMode> from the code, the app would work properly. We got data from the server, and { type: "stop" } message would not be fired.

Screen Shot 2565-05-03 at 18 21 45

Versions

  System:
    OS: macOS 12.3.1
  Binaries:
    Node: 16.14.2 - /usr/local/bin/node
    Yarn: 1.22.18 - ~/.yarn/bin/yarn
    npm: 8.8.0 - /usr/local/bin/npm
  Browsers:
    Chrome: 100.0.4896.127
    Firefox: 99.0
    Safari: 15.4
  npmPackages:
    @apollo/client: ^3.6.2 => 3.6.2

Dependencies in package.json

  "dependencies": {
    "@apollo/client": "^3.6.2",
    "@testing-library/jest-dom": "^5.14.1",
    "@testing-library/react": "^13.0.0",
    "@testing-library/user-event": "^13.2.1",
    "graphql": "^16.4.0",
    "react": "^18.1.0",
    "react-dom": "^18.1.0",
    "react-scripts": "5.0.1",
    "subscriptions-transport-ws": "^0.11.0",
    "web-vitals": "^2.1.0"
  },

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions