react-native-action-cable icon indicating copy to clipboard operation
react-native-action-cable copied to clipboard

Example Implementation for React-Native/Rails/GraphQl/Apollo

Open ccfz opened this issue 5 years ago • 4 comments

We did not find any solutions for our stack React-Native/Rails/GraphQl/Apollo and specifically had problems creating an ActionCable that connected properly with GraphQL subscriptions. The solution was a combination of this very nice package (thanks @kesha-antonov!!) and adjusting graphql-ruby ActionCableLink to work with the interface of react-native-action-cable. In case anyone else is having trouble with this stack I thought it might be nice to see a working example. Maybe in the README or example folder etc.

index.js

/**
 * @format
 */

import React from 'react';
import { ActionCable, Cable } from '@kesha-antonov/react-native-action-cable';
import { ApolloProvider } from 'react-apollo';
import { ApolloClient } from 'apollo-client';
import { ApolloLink } from 'apollo-link';
import { AppRegistry } from 'react-native';
import { createHttpLink } from 'apollo-link-http';
import { getMainDefinition } from 'apollo-utilities';
import { InMemoryCache } from 'apollo-cache-inmemory';

import AppWithNavigator from './src/navigators';
import { name as appName } from './app.json';
import ActionCableLink from './src/helpers/ActionCableLink';

const httpLink = createHttpLink({ uri: 'http://localhost:3000/graphql' });
const actionCable = ActionCable.createConsumer('ws://localhost:3000/cable');
const cable = new Cable({});

const hasSubscriptionOperation = ({ query }) => {
  const { kind, operation } = getMainDefinition(query);

  return kind === 'OperationDefinition' && operation === 'subscription';
};

const link = ApolloLink.split(
  hasSubscriptionOperation,
  new ActionCableLink({ actionCable, cable }),
  httpLink
);

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

const AppWithApollo = () => (
  <ApolloProvider client={client}>
    <AppWithNavigator />
  </ApolloProvider>
);

AppRegistry.registerComponent(appName, () => AppWithApollo);

adjusted ActionCableLink.js

import { ApolloLink, Observable } from 'apollo-link';

const printer = require('graphql/language/printer');

function ActionCableLink(options) {
  const { cable, actionCable } = options;
  const { connectionParams = {} } = options;
  const channelName = options.channelName || 'GraphqlChannel';
  const actionName = options.actionName || 'execute';

  return new ApolloLink(operation => (
    new Observable((observer) => {
      const channelId = Math.round(
        Date.now() + Math.random() * 100000
      ).toString(16);

      const channel = cable.setChannel(
        'GraphqlChannel', // channel name to which we will pass data from Rails app with `stream_from`
        actionCable.subscriptions.create({
          channel: channelName,
          channelId,
          ...connectionParams
        })
      );

      /* eslint-disable func-names */
      channel.on('connected', function () {
        this.perform(
          actionName,
          {
            query: operation.query ? printer.print(operation.query) : null,
            variables: operation.variables,
            operationId: operation.operationId,
            operationName: operation.operationName
          }
        );
      }).on('received', function (payload) {
        if (payload.result.data || payload.result.errors) {
          observer.next(payload.result);
        }

        if (!payload.more) {
          this.unsubscribe();
          observer.complete();
        }
      });

      /* eslint-enable func-names */

      return channel;
    })
  ));
}

module.exports = ActionCableLink;

ccfz avatar Jul 31 '19 08:07 ccfz

@ccfz awesome! Thank you!

kesha-antonov avatar Aug 02 '19 07:08 kesha-antonov

This worked for us too, thanks @ccfz!

javiercr avatar Dec 25 '19 19:12 javiercr

Thank you @ccfz, this is awesome. Without this, my subscriptions weren't initially connecting without triggering a refresh or navigation in RN. Very strange.

developius avatar Apr 22 '21 02:04 developius

Solution still working in 2022. Thanks a lot!

glundgrenm avatar Aug 26 '22 19:08 glundgrenm