All files / src subscription-hoc.tsx

82.14% Statements 46/56
63.64% Branches 21/33
77.78% Functions 7/9
91.49% Lines 43/47
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 11727x   27x   27x   27x 27x               27x             6x     6x     12x 12x 12x 6x     6x 6x   6x 6x       6x     6x 6x 6x 6x   6x 6x   6x 3x 3x         9x 9x 9x 9x   9x 1x             9x               19x 19x             19x       19x 19x 19x 19x                 19x         6x     6x      
import * as React from 'react';
import { DocumentNode } from 'graphql';
const hoistNonReactStatics = require('hoist-non-react-statics');
 
import { parser } from './parser';
import { OperationOption, QueryOpts, OptionProps, DataProps } from './types';
import { default as Subscription } from './Subscriptions';
import {
  getDisplayName,
  GraphQLBase,
  calculateVariablesFromProps,
  defaultMapPropsToOptions,
  defaultMapPropsToSkip,
} from './hoc-utils';
 
export function subscribe<
  TProps extends TGraphQLVariables | {} = {},
  TData = {},
  TGraphQLVariables = {},
  TChildProps = Partial<DataProps<TData, TGraphQLVariables>>
>(
  document: DocumentNode,
  IoperationOptions: OperationOption<TProps, TData, TGraphQLVariables, TChildProps> = {},
) {
  // this is memoized so if coming from `graphql` there is nearly no extra cost
  const operation = parser(document);
  // extract options
  const {
    options = defaultMapPropsToOptions,
    skip = defaultMapPropsToSkip,
    alias = 'Apollo',
    shouldResubscribe,
  } = operationOptions;
 
  let mapPropsToOptions = options as (props: any) => QueryOpts;
  Iif (typeof mapPropsToOptions !== 'function') mapPropsToOptions = () => options as QueryOpts;
 
  let mapPropsToSkip = skip as (props: any) => boolean;
  Iif (typeof mapPropsToSkip !== 'function') mapPropsToSkip = () => skip as any;
 
  // allow for advanced referential equality checks
  let lastResultProps: TChildProps | void;
  return (
    WrappedComponent: React.ComponentType<TChildProps & TProps>,
  ): React.ComponentClass<TProps> => {
    const graphQLDisplayName = `${alias}(${getDisplayName(WrappedComponent)})`;
    class GraphQL extends GraphQLBase<TProps, TChildProps, { resubscribe: boolean }> {
      static displayName = graphQLDisplayName;
      static WrappedComponent = WrappedComponent;
      constructor(props: TProps) {
        super(props);
        this.state = { resubscribe: false };
      }
      componentWillReceiveProps(nextProps: TProps) {
        Iif (!shouldResubscribe) return;
        this.setState({
          resubscribe: shouldResubscribe(this.props, nextProps),
        });
      }
 
      render() {
        let props = this.props;
        const shouldSkip = mapPropsToSkip(props);
        const opts = shouldSkip ? Object.create(null) : mapPropsToOptions(props);
 
        if (!shouldSkip && !opts.variables && operation.variables.length > 0) {
          opts.variables = calculateVariablesFromProps(
            operation,
            props,
            graphQLDisplayName,
            getDisplayName(WrappedComponent),
          );
        }
        return (
          <Subscription
            {...opts}
            displayName={graphQLDisplayName}
            skip={shouldSkip}
            subscription={document}
            shouldResubscribe={this.state.resubscribe}
          >
            {({ data, ...r }) => {
              Iif (operationOptions.withRef) {
                this.withRef = true;
                props = Object.assign({}, props, {
                  ref: this.setWrappedInstance,
                });
              }
              // if we have skipped, no reason to manage any reshaping
              Iif (shouldSkip) return <WrappedComponent {...props} />;
              // the HOC's historically hoisted the data from the execution result
              // up onto the result since it was passed as a nested prop
              // we massage the Query components shape here to replicate that
              const result = Object.assign(r, data || {});
              const name = operationOptions.name || 'data';
              let childProps = { [name]: result };
              Iif (operationOptions.props) {
                const newResult: OptionProps<TProps, TData> = {
                  [name]: result,
                  ownProps: props as TProps,
                };
                lastResultProps = operationOptions.props(newResult, lastResultProps);
                childProps = lastResultProps;
              }
 
              return <WrappedComponent {...props} {...childProps} />;
            }}
          </Subscription>
        );
      }
    }
 
    // Make sure we preserve any custom statics on the original component.
    return hoistNonReactStatics(GraphQL, WrappedComponent, {});
  };
}