Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using `react-apollo-hooks` and `useSubscription` hook

I'm building a simple todo app using React, Apollo and react-apollo-hooks for hooks support, but the useSubscription hook doesnt fire.

I know the actual backend stuff works, because I have a graphiql app set up, and whenever I save a todo, the todoCreated event shows up in graphiql. I also know that the websocket-setup is working properly, because the queries & mutations are going through the websocket. I'm using Elixir, Phoenix, Absinthe, by the way, for the backend stuff.

Here's the Todo-app component:

import React, { useState } from 'react';
import gql from 'graphql-tag';
import { useQuery, useMutation, useSubscription } from 'react-apollo-hooks';

import styles from 'styles.css';

const TODO_FRAGMENT = gql`
  fragment TodoFields on Todo {
    id
    description
  }
`;

const GET_TODOS = gql`
  {
    todos {
      ...TodoFields
    }
  }
  ${TODO_FRAGMENT}
`;

const SAVE_TODO = gql`
  mutation createTodo($description: String!) {
    createTodo(description: $description) {
      ...TodoFields
    }
  }
  ${TODO_FRAGMENT}
`;

const DELETE_TODO = gql`
  mutation deleteTodo($id: ID!) {
    deleteTodo(id: $id) {
      id
    }
  }
`;

const NEW_TODO_SUBSCRIPTION = gql`
  subscription {
    todoCreated {
      ...TodoFields
    }
  }
  ${TODO_FRAGMENT}
`;

const Todos = () => {
  const [inputValue, setInputValue] = useState('');
  const { data, error, loading } = useQuery(GET_TODOS);

  const saveTodo = useMutation(SAVE_TODO, {
    update: (proxy, mutationResult) => {
      proxy.writeQuery({
        query: GET_TODOS,
        data: { todos: data.todos.concat([mutationResult.data.createTodo]) },
      });
    },
  });

  const deleteTodo = useMutation(DELETE_TODO, {
    update: (proxy, mutationResult) => {
      const id = mutationResult.data.deleteTodo.id
      proxy.writeQuery({
        query: GET_TODOS,
        data: { todos: data.todos.filter(item => item.id !== id) },
      });
    },
  });

  const subData = useSubscription(NEW_TODO_SUBSCRIPTION);
  console.log(subData);

  if (loading) {
    return <div>Loading...</div>;
  };

  if (error) {
    return <div>Error! {error.message}</div>;
  };

  return (
    <>
      <h1>Todos</h1>
      {data.todos.map((item) => (
        <div key={item.id} className={styles.item}>
          <button onClick={() => {
            deleteTodo({
              variables: {
                id: item.id,
              },
            });
          }}>Delete</button>
          {' '}
          {item.description}
        </div>
      ))}
      <input
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
        type="text"
      />
      <button onClick={() => {
        saveTodo({
          variables: {
            description: inputValue,
          },
        });
        setInputValue('');
      }}>Save</button>
    </>
  );
};

export default Todos;

And here's the root component:

import React from 'react';
import { ApolloProvider } from 'react-apollo';
import { ApolloProvider as ApolloHooksProvider } from 'react-apollo-hooks';

import Todos from 'components/Todos';
import apolloClient from 'config/apolloClient';

const App = () => (
  <ApolloHooksProvider client={apolloClient}>
    <Todos />
  </ApolloHooksProvider>
);

export default App;

Anyone have a clue on what I seem to be doing wrong?

like image 618
gunnar2k Avatar asked Jul 09 '26 17:07

gunnar2k


1 Answers

Sorry, I figured it out, it was a silly mistake on my part. The problem seems to have been with my apolloClient setup:

import { split } from 'apollo-link';
import { getMainDefinition } from 'apollo-utilities';
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';
import { onError } from 'apollo-link-error';
import { ApolloLink } from 'apollo-link';

import absintheSocketLink from 'config/absintheSocketLink';

const apolloClient = new ApolloClient({
  link: ApolloLink.from([
    onError(({ graphQLErrors, networkError }) => {
      if (graphQLErrors)
        graphQLErrors.map(({ message, locations, path }) =>
          console.log(
            `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
          ),
        );
      if (networkError) console.log(`[Network error]: ${networkError}`);
    }),
    split(
      // split based on operation type
      ({ query }) => {
        const definition = getMainDefinition(query);
        return (
          definition.kind === 'OperationDefinition' &&
          definition.operation === 'subscription'
        );
      },
      new HttpLink({
        uri: 'http://localhost:4000/api/graphql',
        credentials: 'same-origin'
      }),
      absintheSocketLink,
    ),
  ]),
  cache: new InMemoryCache()
});

export default apolloClient;

The error in the code above is the fact that the line

      absintheSocketLink,

is in the wrong place. It should've been before the HttpLink.

Silly me.

like image 88
gunnar2k Avatar answered Jul 11 '26 18:07

gunnar2k