应用apollo的graphql()HOC

时间:2017-08-20 08:25:27

标签: reactjs flowtype react-apollo

在阅读了post关于向graphql注入的对象添加流类型是多么容易之后,我想我会尝试一下。由于6月Flow的帖子已经有了0.53的重大升级,我无法获得使用类组件的基本示例。我不确定它是react-apollo原始定义中的升级还是某些内容尚未确定。

问题:如何让组件的类版本起作用?

这是我的示例代码:

// @flow
import * as React from 'react';
import gql from 'graphql-tag';
import { graphql } from 'react-apollo';
import type { OperationComponent, QueryOpts } from 'react-apollo';

const HERO_QUERY = gql`
  query GetCharacter($episode: Episode!) {
    hero(episode: $episode) {
      name
      id
      friends {
        name
        id
        appearsIn
      }
    }
  }
`;
type Hero = {
  name: string,
  id: string,
  appearsIn: string[],
  friends: Hero[]
};
type Response = {
  hero: Hero
};

type Props = {
  data: QueryOpts & Response,
}

const withCharacter: OperationComponent<Response, Props> = graphql(HERO_QUERY, {
  options: () => ({
    variables: { episode: 'JEDI' },
  }),
});

class TestClass extends React.Component<Props> {
  render() {
    const { loading, error, hero } = this.props.data;
    if (loading) return <div>Loading</div>;
    if (error) return <h1>ERROR</h1>;
    return <div>My hero: {hero.name}</div>;
  }
}

const TestFn = ({ data: { loading, error, hero } }) => {
  if (loading) return <div>Loading</div>;
  if (error) return <h1>ERROR</h1>;
  return <div>My hero: {hero.name}</div>;
};

export const clss = withCharacter(TestClass); // ERROR!
export const fn = withCharacter(TestFn); // Works fine

以下是OperationComponent类型的定义,以及来自definition的核心部分,其更新与0.53 Flow样式相匹配:

export interface QueryProps {
  error?: ApolloError,
  networkStatus: number,
  loading: boolean,
  variables: Object,
  fetchMore: (
    fetchMoreOptions: FetchMoreQueryOptions & FetchMoreOptions,
  ) => Promise<ApolloQueryResult<any>>,
  refetch: (variables?: Object) => Promise<ApolloQueryResult<any>>,
  startPolling: (pollInterval: number) => void,
  stopPolling: () => void,
  subscribeToMore: (options: SubscribeToMoreOptions) => () => void,
  updateQuery: (
    mapFn: (previousQueryResult: any, options: UpdateQueryOptions) => any,
  ) => void,
}

export type MutationFunc<TResult> = (
  opts: MutationOpts,
) => Promise<ApolloQueryResult<TResult>>;

export type ChildProps<P, R> = {
  data: QueryProps & R,
  mutate: MutationFunc<R>,
} & P;

export interface OperationComponent<
  TResult: Object = {},
  TOwnProps: Object = {},
  TMergedProps = ChildProps<TOwnProps, TResult>,
> {
  (
    component:
      | StatelessComponent<TMergedProps>
      | Class<React$Component<any, TMergedProps, any>>,
  ): Class<React$Component<TOwnProps, void>>,
}

declare export function graphql<TResult, TProps, TChildProps>(
  document: DocumentNode,
  operationOptions?: OperationOption<TProps, TResult>,
): OperationComponent<TResult, TProps, TChildProps>;

当我运行flow focus-check src/test.js(v。0.53.1)时,我得到:

Error: src/test.js:42
 42:     const { loading, error, hero } = this.props.data;
                 ^^^^^^^ property `loading`. Property cannot be accessed on any member of intersection type
 42:     const { loading, error, hero } = this.props.data;
                                          ^^^^^^^^^^^^^^^ intersection
  Member 1:
   31:   data: QueryOpts & Response,
               ^^^^^^^^^ QueryOpts
  Error:
   42:     const { loading, error, hero } = this.props.data;
                   ^^^^^^^ property `loading`. Property not found in
   31:   data: QueryOpts & Response,
               ^^^^^^^^^ QueryOpts
  Member 2:
   31:   data: QueryOpts & Response,
                           ^^^^^^^^ Response
  Error:
   42:     const { loading, error, hero } = this.props.data;
                   ^^^^^^^ property `loading`. Property not found in
   31:   data: QueryOpts & Response,
                           ^^^^^^^^ object type

Error: src/test.js:42
 42:     const { loading, error, hero } = this.props.data;
                          ^^^^^ property `error`. Property cannot be accessed on any member of intersection type
 42:     const { loading, error, hero } = this.props.data;
                                          ^^^^^^^^^^^^^^^ intersection
  Member 1:
   31:   data: QueryOpts & Response,
               ^^^^^^^^^ QueryOpts
  Error:
   42:     const { loading, error, hero } = this.props.data;
                            ^^^^^ property `error`. Property not found in
   31:   data: QueryOpts & Response,
               ^^^^^^^^^ QueryOpts
  Member 2:
   31:   data: QueryOpts & Response,
                           ^^^^^^^^ Response
  Error:
   42:     const { loading, error, hero } = this.props.data;
                            ^^^^^ property `error`. Property not found in
   31:   data: QueryOpts & Response,
                           ^^^^^^^^ object type

Error: src/test.js:55
 55: export const clss = withCharacter(TestClass);
                                       ^^^^^^^^^ class type: TestClass. This type is incompatible with
             v-------------------------------
123:       | StatelessComponent<TMergedProps>
124:       | Class<React$Component<any, TMergedProps, any>>,
           -----------------------------------------------^ union: type application of polymorphic type: type `StatelessComponent` | class type: type application of identifier `React$Component`. See: node_modules/react-apollo/react-apollo.umd.js.flow:123
  Member 1:
  123:       | StatelessComponent<TMergedProps>
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type application of polymorphic type: type `StatelessComponent`. See: node_modules/react-apollo/react-apollo.umd.js.flow:123
  Error:
  123:       | StatelessComponent<TMergedProps>
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function type. Callable signature not found in. See: node_modules/react-apollo/react-apollo.umd.js.flow:123
   55: export const clss = withCharacter(TestClass);
                                         ^^^^^^^^^ statics of TestClass
  Member 2:
  124:       | Class<React$Component<any, TMergedProps, any>>,
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ class type: type application of identifier `React$Component`. See: node_modules/react-apollo/react-apollo.umd.js.flow:124
  Error:
  124:       | Class<React$Component<any, TMergedProps, any>>,
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type application of identifier `React$Component`. Too many type arguments. Expected at most 2. See: node_modules/react-apollo/react-apollo.umd.js.flow:124
   29: declare class React$Component<Props, State = void> {
                                     ^^^^^^^^^^^^ See type parameters of definition here. See lib: /tmp/flow/flowlib_1135e841/react.js:29


Found 3 errors

我尝试添加了0.53项改进,但无济于事:

export interface OperationComponent<
  TResult: Object = {},
  TOwnProps: Object = {},
  TMergedProps = ChildProps<TOwnProps, TResult>,
> {
  (
    component: React$ComponentType<TMergedProps>
  ): React$ComponentType<TOwnProps>,
}

1 个答案:

答案 0 :(得分:1)

哦......错误的查询,应该是QueryProps而不是QueryOpts

// @flow
import * as React from 'react';
import gql from 'graphql-tag';
import { graphql } from 'react-apollo';
import type { OperationComponent, QueryProps } from 'react-apollo';

const HERO_QUERY = gql`
  query GetCharacter($episode: Episode!) {
    hero(episode: $episode) {
      name
      id
      friends {
        name
        id
        appearsIn
      }
    }
  }
`;

type Hero = {
  name: string,
  id: string,
  appearsIn: string[],
  friends: Hero[]
};

type Response = {
  hero: Hero
};

type Props = {
  data: QueryProps & Response,
  mutate: any,
}

const withCharacter: OperationComponent<Response, Props> = graphql(HERO_QUERY, {
  options: () => ({
    variables: { episode: 'JEDI' },
  }),
});

class TestClass extends React.Component<Props> {
  render() {
    const { loading, error, hero } = this.props.data;
    if (loading) return <div>Loading</div>;
    if (error) return <h1>ERROR</h1>;
    return <div>My hero: {hero.name}</div>;
  }
}

const TestFn = ({ data: { loading, error, hero } }) => {
  if (loading) return <div>Loading</div>;
  if (error) return <h1>ERROR</h1>;
  return <div>My hero: {hero.name}</div>;
};

export const clss = withCharacter(TestClass); // Works
export const fn = withCharacter(TestFn); // Works