diff --git a/packages/apollo-fragment-react/package.json b/packages/apollo-fragment-react/package.json index 55c8278..aff246d 100644 --- a/packages/apollo-fragment-react/package.json +++ b/packages/apollo-fragment-react/package.json @@ -1,7 +1,7 @@ { "name": "apollo-fragment-react", "version": "0.7.0", - "description": "A Query component to connect React components to GraphQL fragments", + "description": "A React hook to connect React components to GraphQL fragments in Apollo Client cache", "author": "Abhi Aiyer ", "license": "MIT", "main": "./lib/bundle.umd.js", @@ -42,10 +42,10 @@ } ], "peerDependencies": { - "apollo-link-state": "0.4.1", - "react-apollo": "^3.0.0-beta.3" + "@apollo/client": "^3.2.0" }, "devDependencies": { + "@apollo/client": "^3.2.0", "@types/graphql": "0.11.5", "@types/jest": "22.1.x", "apollo-cache-inmemory": "^1.1.5", @@ -64,7 +64,6 @@ "pre-commit": "1.2.2", "prettier": "1.7.4", "react": "^16.8.6", - "react-apollo": "^3.0.0-beta.3", "react-dom": "^16.8.6", "rimraf": "2.6.1", "rollup": "0.56.x", diff --git a/packages/apollo-fragment-react/src/__tests__/index.tsx b/packages/apollo-fragment-react/src/__tests__/index.tsx index d85c8fd..b8f73f7 100644 --- a/packages/apollo-fragment-react/src/__tests__/index.tsx +++ b/packages/apollo-fragment-react/src/__tests__/index.tsx @@ -1,16 +1,9 @@ import * as React from 'react'; -import gql from 'graphql-tag'; -import { ApolloLink } from 'apollo-link'; -import { ApolloClient } from 'apollo-client'; -import { ApolloProvider } from 'react-apollo'; -import { InMemoryCache } from 'apollo-cache-inmemory'; +import { ApolloClient, InMemoryCache, ApolloLink, gql } from '@apollo/client'; +import { ApolloProvider } from '@apollo/client/react'; import { mount, ReactWrapper } from 'enzyme'; import mockLink from '../mocks/mockLink'; -import { - fragmentCacheRedirect, - fragmentLinkState, -} from '../../../apollo-link-state-fragment/src'; -import { ApolloFragment, withApolloFragment, useApolloFragment } from '../'; +import { fragmentCacheConfig, useApolloFragment } from '../'; type Wrapper = ReactWrapper | null; @@ -28,18 +21,12 @@ const fragment = ` const createTestClient = () => { const cache = new InMemoryCache({ - cacheRedirects: { - Query: { - ...fragmentCacheRedirect(), - }, - }, + ...fragmentCacheConfig, }); - const local = fragmentLinkState(cache); - return new ApolloClient({ cache, - link: ApolloLink.from([local, mockLink]), + link: ApolloLink.from([mockLink]), }); }; @@ -68,73 +55,6 @@ describe('apollo-fragment-react core behaviour', () => { } `; - it('Should return Fragment Data from HOC Component', () => { - return client - .query({ - query: PEOPLE_QUERY, - }) - .then(() => { - let SomeComponent: React.ComponentType = function Foo(props) { - expect(props.data.id).toEqual('1'); - expect(props.data.name).toEqual('John Smith'); - return

hi

; - }; - - SomeComponent = withApolloFragment(fragment)(SomeComponent); - - wrapper = mount( - - - , - ); - }); - }); - - it('Should return Fragment Data from HOC Component with custom "id"', () => { - return client - .query({ - query: PEOPLE_QUERY, - }) - .then(() => { - let SomeComponent: React.ComponentType = function Foo(props) { - expect(props.data.id).toEqual('1'); - expect(props.data.name).toEqual('John Smith'); - return

hi

; - }; - - SomeComponent = withApolloFragment( - fragment, - 'fragmentId', - )(SomeComponent); - - wrapper = mount( - - - , - ); - }); - }); - - it('Should return Fragment Data from Render Prop Component', () => { - return client - .query({ - query: PEOPLE_QUERY, - }) - .then(() => { - wrapper = mount( - - - {(result: any) => { - expect(result.data.id).toEqual('1'); - expect(result.data.name).toEqual('John Smith'); - return

hi

; - }} -
-
, - ); - }); - }); - it('Should return Fragment Data from React Hook', () => { return client .query({ @@ -233,65 +153,6 @@ Available data: Make sure that the fields requested in the fragment are fetched by some query`; - it('Should log error when fragment data is incomplete for HOC Component', () => { - return client - .query({ - query: INCOMPLETE_QUERY, - }) - .then(() => { - let SomeComponent: React.ComponentType = function Foo(props) { - expect(props.data).toBeUndefined(); - return

hi

; - }; - - SomeComponent = withApolloFragment(fragment)(SomeComponent); - - wrapper = mountWithApollo(); - - expect(consoleOutput[0]).toEqual(expectedErrorMessage); - }); - }); - - it('Should log error when fragment data is incomplete for HOC Component with custom "id"', () => { - return client - .query({ - query: INCOMPLETE_QUERY, - }) - .then(() => { - let SomeComponent: React.ComponentType = function Foo(props) { - expect(props.data).toBeUndefined(); - return

hi

; - }; - - SomeComponent = withApolloFragment( - fragment, - 'fragmentId', - )(SomeComponent); - - wrapper = mountWithApollo(); - - expect(consoleOutput[0]).toEqual(expectedErrorMessage); - }); - }); - - it('Should log error when fragment data is incomplete for Render Prop Component', () => { - return client - .query({ - query: INCOMPLETE_QUERY, - }) - .then(() => { - wrapper = mountWithApollo( - - {(result: any) => { - expect(result.data).toEqual({}); - return

hi

; - }} -
, - ); - expect(consoleOutput[0]).toEqual(expectedErrorMessage); - }); - }); - it('Should log error when fragment data is incomplete for hook', () => { return client .query({ @@ -331,44 +192,6 @@ Make sure that the fields requested in the fragment are fetched by some query`; }); }); - it('Should NOT log error when fragment data is empty for HOC Component', () => { - let SomeComponent: React.ComponentType = function Foo(props) { - expect(props.data).toBeUndefined(); - return

hi

; - }; - - SomeComponent = withApolloFragment(fragment)(SomeComponent); - - wrapper = mountWithApollo(); - - expect(consoleOutput).toHaveLength(0); - }); - - it('Should NOT log error when fragment data is empty for HOC Component with custom "id"', () => { - let SomeComponent: React.ComponentType = function Foo(props) { - expect(props.data).toBeUndefined(); - return

hi

; - }; - - SomeComponent = withApolloFragment(fragment, 'fragmentId')(SomeComponent); - - wrapper = mountWithApollo(); - - expect(consoleOutput).toHaveLength(0); - }); - - it('Should NOT log error when fragment data is empty for Render Prop Component', () => { - wrapper = mountWithApollo( - - {(result: any) => { - expect(result.data).toEqual({}); - return

hi

; - }} -
, - ); - expect(consoleOutput).toHaveLength(0); - }); - it('Should NOT log error when fragment data is empty for hook', () => { let SomeComponent = function Foo() { const fragmentData = useApolloFragment(fragment, '1'); diff --git a/packages/apollo-fragment-react/src/index.tsx b/packages/apollo-fragment-react/src/index.tsx index 68414e0..e216f34 100644 --- a/packages/apollo-fragment-react/src/index.tsx +++ b/packages/apollo-fragment-react/src/index.tsx @@ -1,21 +1,8 @@ import * as React from 'react'; -import { - Query, - graphql, - useQuery, - withApollo, - QueryResult, -} from 'react-apollo'; +import { ApolloClient, InMemoryCacheConfig } from '@apollo/client'; +import { useQuery, QueryResult } from '@apollo/client/react'; import { DocumentNode, Location } from 'graphql'; import { getFragmentInfo, buildFragmentQuery } from 'apollo-fragment-utils'; -import ApolloClient from 'apollo-client'; -// compose-tiny doesn't have a default export, so we have to use `* as` -// However, rollup creates a synthetic default module, so we have to to import it with `default as`. -import * as _compose from 'compose-tiny'; -// @ts-ignore -import { default as _rollupCompose } from 'compose-tiny'; - -const compose = _rollupCompose || _compose; type FragmentQueryData = { getFragment?: TData; @@ -68,87 +55,24 @@ export function useApolloFragment( }; } -export function withApolloFragment( - fragment: SupportedFragment, - idPropName: string = 'id', -) { - const fragmentQuery = createFragmentQuery(fragment); - return compose( - withApollo, - graphql(fragmentQuery.query, { - options: props => { - return { - fetchPolicy: 'cache-only', - variables: { - id: props[idPropName], - __typename: fragmentQuery.fragmentTypeName, - }, - }; - }, - props: ({ data, ownProps: { client, ...ownProps }, ...rest }) => { - const fragmentData = data && data.getFragment; - - if (!fragmentData) { - checkDataCompleteness({ - fragmentQuery, - id: ownProps[idPropName], - client, +export const fragmentCacheConfig: Required> = { + typePolicies: { + Query: { + fields: { + getFragment(_, { args, toReference }) { + return toReference({ + id: args ? args.id : undefined, + __typename: args ? args.__typename : undefined, }); - } - - return { - data: fragmentData, - ownProps, - ...rest, - }; + }, }, - }), - ); -} - -type ApolloFragmentChildrenData = Omit< - ApolloFragmentResult, - 'data' -> & { data: ApolloFragmentResult['data'] | {} }; - -export type ApolloFragmentProps = { - id: string; - fragment: SupportedFragment; - children: ( - fragmentQueryResult: ApolloFragmentChildrenData, - ) => React.ReactElement; + }, + }, }; -export function ApolloFragment({ - children, - fragment, - id, -}: ApolloFragmentProps) { - const fragmentQuery = createFragmentQuery(fragment); - - return ( - > - fetchPolicy="cache-only" - query={fragmentQuery.query} - variables={{ id, __typename: fragmentQuery.fragmentTypeName }} - > - {({ data, client, ...rest }) => { - const fragmentData = data && data.getFragment; - - if (!fragmentData) { - checkDataCompleteness({ - fragmentQuery, - id, - client, - }); - } - - return children({ data: fragmentData || {}, client, ...rest }); - }} - - ); -} - type FragmentQuery = { query: DocumentNode; fragmentTypeName: string; diff --git a/packages/apollo-fragment-react/src/mocks/mockLink.ts b/packages/apollo-fragment-react/src/mocks/mockLink.ts index 6a49d03..b9156b6 100644 --- a/packages/apollo-fragment-react/src/mocks/mockLink.ts +++ b/packages/apollo-fragment-react/src/mocks/mockLink.ts @@ -1,12 +1,5 @@ import { graphql, print, ExecutionResult } from 'graphql'; -import { - Operation, - GraphQLRequest, - ApolloLink, - FetchResult, - Observable, - // Observer, -} from 'apollo-link'; +import { ApolloLink, FetchResult, Observable } from '@apollo/client'; import { schema } from './mockSchema'; export default new ApolloLink(operation => {