loading...

Using React Hooks With Apollo

jakesweb profile image Jacob Colborn ・4 min read

My Setup

I am using React with Apollo to connect to a GraphQL API utilizing Nextjs to handle the rendering and routing. This means I have my Apollo client registered under _app.js for Nextjs. The application I am building is an article submission website, similar to Dev.to, but for esports commentary and news. The package being used is the react-apollo package that has a react-hooks dependency for us to exploit.

Why I Am Writing This

Everyone may be thinking this is a basic thing, and that's what I thought too. I was trying to find the best way to build out a Signout component. I didn't want to create a whole page for Signout, so I set it up as an Apollo Link that has an anchor tag embedded. On the anchor, there is an onClick handler that calls my useSignout hook. Getting it working as a Link and anchor I was able to style it like the rest of my navigation bar so I could just add it as an option once the user is logged in. The useSignout hook I created utilizes the useMutation hook from the react-hooks package. The code kinda speaks for itself:

import { useMutation } from "@apollo/react-hooks";
import gql from "graphql-tag";
import Link from "next/link";

const SIGNOUT_MUTATION = gql`
  mutation SIGNOUT_MUTATION {
    signout {
      message
    }
  }
`;

export default function Signout() {
  const [useSignout] = useMutation(SIGNOUT_MUTATION, {
    onCompleted: () => {
      sessionStorage.clear();
      history.go(0);
    }
  });

  return (
    <Link href="/">
      <a
        onClick={() => {
          useSignout();
        }}
      >
        Signout
      </a>
    </Link>
  );
}

You notice that it still has all the normal flow of a React hook and Apollo mutation. So I created this and went running my Apollo application and what do you know ... an error. After searching for about a half-hour I finally found the answer, using a hook requires Apollo to know there is a hook coming. Now, I will preface the rest of this by saying that this was my first attempt at using hooks. Maybe if I had known how React hooks were supposed to function, this would have been a non-issue, but here we are.

The Reason to Use a Hook Here

The reason that I wanted to use a hook is that I didn't want to wrap my whole <Link><a></a><Link> flow in a weird kind of form. That wouldn't even work, was my first thought, but looking back (hindsight is the keyword for the year 2020 I believe) I can think of a way to put that in, but why not try out a hook for the first time? Also, this matched with the rest of my navigation (where I put the signout link) so the styling was consistent.

The Fix

Most people more familiar with Apollo and hooks will have seen this coming, but for us who are going on the first foray with hooks and Apollo, quickly learn we need to wrap our application in an Apollo Provider component from the react-hooks package in react-apollo. Originally my code looked like this:

class myApp extends App {
  ...
  return (
   <Container>
     <ApolloProvider client={apollo}>
       <Page>
         <Component {...pageProps} />
       </Page>
     </ApolloProvider>
   </Container> 
 )
}

Quickly, I found the issue. <ApolloProvider> could not handle a hook by itself. This component could grab all of the other Apollo 'stuff' coming from our application, but this didn't fit with the rules for using hooks. If you are curious about what the full rules are, check out the Rules of Hooks from the ReactJS documentation. I didn't full deconstruct what rule this broke but I believe it is because the Apollo Hooks Provider is a React function component where the Apollo Provider component is a container component (I want to point out this could be very wrong, being my first React hook, feel free to let me know if that is way out of left field). After finding the react-hooks dependency on react-apollo I found the solution. First I had to import the Apollo hook provider. I created a new ApolloProvider with the react-hooks package

import { ApolloProvider as ApolloProviderHooks } from "@apollo/react-hooks";

As you can see from above, it is just another ApolloProvider but this is being instantiated from the react-hooks package inside of the react-apollo package. Now I just take my same code as before and wrap the Page component above the ApolloProviderHooks component.

class myApp extends App {
  ...
  return (
   <Container>
     <ApolloProvider client={apollo}>
       <ApolloProviderHooks client={apollo}>
         <Page>
           <Component {...pageProps} />
         </Page>
       </ApolloProviderHooks>
     </ApolloProvider>
   </Container> 
 )
}

What I Learned

The main learning experience here is if I am going to dive into some new technology, I should probably do more than just think, "Hey, I've heard of hooks, I think this is my solution." Here comes hindsight once again, read a document before trying it out. Like I said earlier, this was just me getting my feet wet with React hooks. I could have built it the same way as I built all my other Mutations, but I grew from this experience. If anyone has any easier ways to handle this, let me know. This is all for learning so the more I (we) all know the better. Have a great day and thanks for the read!

Posted on by:

jakesweb profile

Jacob Colborn

@jakesweb

Trying to learn javascript and taking everyone on the ride with me.

Discussion

markdown guide