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

迁移Apollo Client 3.0后useMutation钩子调用报错及解决办法

Fixing "Invalid hook call" Error with Apollo Client 3.0 in Class Components

I recently migrated my app to Apollo Client 3.0 and hit this frustrating error when trying to use useMutation or useQuery:

Error: Invalid hook call. Hooks can only be called inside of the body of a function component

The error message lists common culprits—mismatched React versions, breaking Hook rules, duplicate React copies—but none of those applied here. I even checked related issues but couldn't find a fix that worked. Eventually, I solved it by using the withApollo higher-order component (HOC) since I was working with class components.


The Problematic Code

I initially tried using useMutation in a function component nested inside my class component, which triggered the error:

import { useMutation } from '@apollo/client';
function LockedBlog() { 
  const [lockedBlog] = useMutation(LOCKED_BLOG); 
};
class EditBlog extends Component { 
  state = { ...initialState }; 
}

My Apollo Client 3.0 Setup (index.js)

For reference, here's how I configured Apollo Client at the root of my app:

import React from "react";
import ReactDOM from "react-dom";
import "./light.scss";
import App from "./components/App";
import { ApolloProvider } from '@apollo/client'
import { ApolloClient } from '@apollo/client/core';
import {ApolloLink} from 'apollo-link';
import {SubscriptionClient} from 'subscriptions-transport-ws';
import {WebSocketLink} from 'apollo-link-ws';
import {createHttpLink} from 'apollo-link-http';
import {InMemoryCache } from '@apollo/client/cache';
import { onError } from 'apollo-link-error';

const errorLink = onError(({ networkError, graphQLErrors }) => {
  if (graphQLErrors) {
    graphQLErrors.map(({ message, locations, path }) =>
      console.log(
        `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
      ),
    );
  }
  if (networkError) console.log(`[Network error]: ${networkError}`);
});

const middlewareLink = new ApolloLink((operation, forward) => {
  operation.setContext({
    headers: { authorization: localStorage.getItem('token') || null }
  });
  return forward(operation);
});

const wsLink = new WebSocketLink(
  new SubscriptionClient("ws://localhost:4004/graphql", {
    reconnect: true,
  }),
);

const httpLink = middlewareLink.concat(
  createHttpLink({ uri: "http://localhost:4004/graphql", })
);

const hasSubscriptionOperation = ({query: {definitions}}) => {
  return definitions.some(({kind, operation}) => kind === 'OperationDefinition' && operation === 'subscription');
}

const link = ApolloLink.split(
  hasSubscriptionOperation,
  wsLink,
  httpLink,
  errorLink,
);

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

ReactDOM.render(
  <React.Fragment>
    <ApolloProvider client={client}>
      <App />
    </ApolloProvider>
  </React.Fragment>,
  document.getElementById("root")
);

Dependencies (package.json snippet)

"@apollo/client": "^3.0.2",
"@apollo/react-hooks": "^3.1.5",
"@material-ui/core": "^4.10.2",
"react": "^16.13.1",
"react-dom": "^16.13.1"

The Solution: Using withApollo HOC

Since React Hooks (like useMutation) can't be used inside class components, the fix was to inject the Apollo Client instance directly into my class component using the withApollo HOC. Here's the working implementation:

import { withApollo } from '@apollo/client/react/hoc';
import { gql } from '@apollo/client'; // Don't forget this import!

class EditBlog extends Component {
  componentDidMount() {
    this.lockedBlog(true);
  }

  componentWillUnmount() {
    this.lockedBlog(false);
  }

  lockedBlog = locked => {
    const { id } = this.props.match.params;
    this.props.client
      .mutate({
        variables: { id, locked },
        mutation: gql`
          mutation($id: ID!, $locked: Boolean!) {
            lockedBlog(id: $id, data: { locked: $locked }) {
              locked
            }
          }
        `
      })
      .then(result => {
        console.log(result);
      })
      .catch(error => {
        console.log(error);
      });
  };

  // Add your other class methods and render logic here...
}

// Wrap the component to inject the Apollo Client via props
export default withApollo(EditBlog);

Key Takeaways

  • Hooks are for function components only: You can't use useMutation or useQuery directly inside class components—this is a core React Hook rule.
  • withApollo bridges class components and Apollo Client: This HOC passes the Apollo Client instance to your class component via this.props.client, letting you call mutate() or query() directly.
  • Ensure your app is wrapped in ApolloProvider: This was already correctly set up in my index.js, but double-check that your root component is wrapped to provide the client to all child components.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 14:32:56