Search code examples
javascriptreactjsgraphqlapollo

I can't use useMutation or useQuery Apollo Client 3.0


  import { useMutation } from '@apollo/client';;
        
  function LockedBlog() {
  const [lockedBlog] = useMutation(LOCKED_BLOG);
  };

  class EditBlog extends Component {
    state = {
      ...initialState
    };

index.js

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")
);

my package.json

"@apollo/client": "^3.0.2",
"@apollo/react-hooks": "^3.1.5",
"@material-ui/core": "^4.10.2",
"@material-ui/icons": "^4.9.1",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"Dante2": "^0.5.0-rc4",
"apollo-boost": "^0.4.9",
"apollo-cache-inmemory": "^1.6.6",
"apollo-client": "^2.6.10",
"apollo-link": "^1.2.14",
"apollo-link-error": "^1.1.13",
"apollo-link-http": "^1.5.17",
"apollo-link-ws": "^1.0.20",
"apollo-upload-client": "^13.0.0",
"env-cmd": "^10.1.0",
"graphql": "^15.1.0",
"graphql-tag": "^2.10.4",
"jsonwebtoken": "^8.5.1",
"lodash.flowright": "^3.5.0",
"moment": "^2.27.0",
"node-sass": "^4.14.1",
"prismjs": "^1.20.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-dropzone": "^11.0.1",
"react-router-dom": "^5.2.0",
"react-scripts": "3.4.1",
"subscriptions-transport-ws": "^0.9.16"

i get this error when i use it. i migrated apollo client 3.0

Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:

  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app

there are many people who get the same error here. and no solution was found. https://github.com/apollographql/react-apollo/issues/3454

SOLVED

import { withApollo } from '@apollo/client/react/hoc';

// class 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);
      });
  };

and export

export default withApollo(EditBlog);

Solution

  • You might be breaking the Rules of Hooks

    Hooks can't be used within event handlers - you can use them only within 'rendering flow' (main FC body) ... and there is requirement for constant order of calling hooks ... so not in conditional blocks (functions/handlers/etc.).

    update:

    const LockedBlog = (props) => {
      const [lockedBlog] = useMutation(LOCKED_BLOG);
      
      lockedBlogHandler = (e, someId) => {
        // const { id } = props.match.params;
        lockedBlog({variables:{ Your data that you have passed }}) // call invoker returned from useMutation
        // with variables prepared from component props, handler passed someId, etc.
        console.log(someId);
      };
      return (
        <Button onClick={lockedBlogHandler} >
        // search docs how to pass args into handler