From 726f2655085ad1514b42547cd4b9b9690c2434d2 Mon Sep 17 00:00:00 2001 From: Jeremy Durnell Date: Sun, 16 Jun 2019 21:51:04 -0500 Subject: [PATCH] updated to use latest libraries/versions --- README.md | 82 +-- package.json | 3 +- src/SuspenseSSR.tsx | 12 - src/__tests__/SuspenseSSR-test.tsx | 112 ---- src/__tests__/useApolloClient-test.tsx | 2 +- src/__tests__/useMutation-test.tsx | 2 +- src/__tests__/useQuery-test.tsx | 771 ++++--------------------- src/__tests__/useSubscription-test.tsx | 2 +- src/index.ts | 1 - src/internal/__mocks__/actHack.ts | 2 +- src/queryCache.ts | 6 +- src/useMutation.ts | 5 +- src/useQuery.ts | 33 +- tslint.json | 2 +- yarn.lock | 138 ++++- 15 files changed, 235 insertions(+), 938 deletions(-) delete mode 100644 src/SuspenseSSR.tsx delete mode 100644 src/__tests__/SuspenseSSR-test.tsx diff --git a/README.md b/README.md index 5298b69..72ea70d 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,6 @@ Use [Apollo Client](https://github.com/apollographql/apollo-client) as React Or if using [yarn](https://yarnpkg.com/en/) - `yarn add react-apollo-hooks` # Example @@ -19,6 +18,8 @@ Or if using [yarn](https://yarnpkg.com/en/) is a port of Pupstagram sample app to react-apollo-hooks. +> NOTE: This example is outdated and may not work properly. Support for React Suspense is no longer supported. + # API ## ApolloProvider @@ -97,48 +98,7 @@ const Dogs = () => { const { data, error, loading } = useQuery(GET_DOGS); if (loading) { return
Loading...
; - }; - if (error) { - return
Error! {error.message}
; - }; - - return ( - - ); -}; - -``` - -### Usage with Suspense (experimental) - -You can use `useQuery` with [React Suspense](https://www.youtube.com/watch?v=6g3g0Q_XVb4) -with the `{ suspend: true }` option. -Please note that it's not yet recommended to use it in production. Please look -at the [issue #69](https://github.com/trojanowski/react-apollo-hooks/issues/69) -for details. - -Example usage: - -```javascript -import gql from 'graphql-tag'; -import React, { Suspense } from 'react'; -import { useQuery } from 'react-apollo-hooks'; - -const GET_DOGS = gql` - { - dogs { - id - breed - } } -`; - -const Dogs = () => { - const { data, error } = useQuery(GET_DOGS, { suspend: true }); if (error) { return
Error! {error.message}
; } @@ -151,19 +111,8 @@ const Dogs = () => { ); }; - -const MyComponent = () => ( - Loading...}> - - -); ``` -There are known issues with suspense mode for `useQuery`: - -* only the `cache-first` fetch policy is supported ([#13](https://github.com/trojanowski/react-apollo-hooks/issues/13)) -* `networkStatus` returned by `useQuery` is undefined ([#68](https://github.com/trojanowski/react-apollo-hooks/pull/68)) - ## useMutation ```javascript @@ -250,14 +199,14 @@ const NewMessagesIndicator = () => { if (loading) { return
Loading...
; - }; + } if (error) { return
Error! {error.message}
; - }; + } return
{data.newMessagesCount} new messages
; -} +}; ``` For more advanced use cases, e. g. when you'd like to show a notification @@ -275,7 +224,7 @@ const { data, error, loading } = useSubscription(MY_SUBSCRIPTION, { // data and the Apollo client. You can use methods of the client to update // the Apollo cache: // https://www.apollographql.com/docs/react/advanced/caching.html#direct - } + }, // ... rest options }); ``` @@ -335,22 +284,3 @@ app.get('/', async (req, res) => { res.send(renderedHtml); }); ``` - -`getMarkupFromTree` supports `useQuery` hooks invoked in both suspense -and non-suspense mode, but the [React.Suspense](https://reactjs.org/docs/react-api.html#reactsuspense) -component is not supported. You can use `unstable_SuspenseSSR` provided -by this library instead: - -```javascript -import { unstable_SuspenseSSR as UnstableSuspenseSSR } from 'react-apollo-hooks'; - -function MyComponent() { - return ( - }> -
- -
-
- ); -} -``` diff --git a/package.json b/package.json index 2e03e05..1093e92 100644 --- a/package.json +++ b/package.json @@ -81,9 +81,11 @@ "@babel/preset-env": "^7.1.0", "@babel/preset-react": "^7.0.0", "@babel/preset-typescript": "^7.1.0", + "@testing-library/react": "^8.0.1", "@types/graphql": "^14.0.3", "@types/jest": "^23.3.10", "@types/lodash": "^4.14.119", + "@types/node": "^12.0.8", "@types/react": "^16.8.2", "@types/react-dom": "^16.8.0", "apollo-cache-inmemory": "^1.3.11", @@ -103,7 +105,6 @@ "prettier": "^1.15.2", "react": "16.8.1", "react-dom": "16.8.1", - "react-testing-library": "^5.5.3", "rimraf": "^2.6.2", "standard-version": "^4.4.0", "tslint": "^5.12.0", diff --git a/src/SuspenseSSR.tsx b/src/SuspenseSSR.tsx deleted file mode 100644 index eec0698..0000000 --- a/src/SuspenseSSR.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React, { Suspense, SuspenseProps, useContext } from 'react'; -import { SSRContext } from './internal/SSRContext'; - -export function unstable_SuspenseSSR({ children, fallback }: SuspenseProps) { - const ssrManager = useContext(SSRContext); - - return ssrManager ? ( - <>{children} - ) : ( - {children} - ); -} diff --git a/src/__tests__/SuspenseSSR-test.tsx b/src/__tests__/SuspenseSSR-test.tsx deleted file mode 100644 index 71f9944..0000000 --- a/src/__tests__/SuspenseSSR-test.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import ApolloClient from 'apollo-client'; -import { ApolloLink } from 'apollo-link'; -import { MockedResponse } from 'apollo-link-mock'; -import gql from 'graphql-tag'; -import * as React from 'react'; -import { renderToString } from 'react-dom/server'; -import { render } from 'react-testing-library'; - -import { ApolloProvider } from '../ApolloContext'; -import { unstable_SuspenseSSR as SuspenseSSR } from '../SuspenseSSR'; -import createClient from '../__testutils__/createClient'; -import wait from '../__testutils__/wait'; -import { getMarkupFromTree } from '../getMarkupFromTree'; -import { QueryHookOptions, useQuery } from '../useQuery'; - -jest.mock('../internal/actHack'); - -const USER_QUERY = gql` - { - currentUser { - firstName - } - } -`; - -interface UserQueryResult { - currentUser: { firstName: string }; -} - -const MOCKS: MockedResponse[] = [ - { - request: { query: USER_QUERY }, - result: { data: { currentUser: { firstName: 'James' } } }, - }, -]; - -function createMockClient(link?: ApolloLink) { - return createClient({ link, mocks: MOCKS, addTypename: false }); -} - -function UserDetails(props: QueryHookOptions<{}>) { - const { data, loading } = useQuery(USER_QUERY, props); - - return ( - <> - {loading - ? 'Loading' - : !data - ? 'No Data' - : !data.currentUser - ? 'No Current User' - : data.currentUser.firstName} - - ); -} - -interface UserWrapperProps extends QueryHookOptions<{}> { - readonly client: ApolloClient; -} - -function UserDetailsWrapper({ client, ...props }: UserWrapperProps) { - return ( - - Loading with suspense}> - - - - ); -} - -describe.each([[true], [false]])('SuspenseSSR with "suspend: %s"', suspend => { - it('not throws in react-dom', async () => { - const client = createMockClient(); - - const { container } = render( - - ); - - if (suspend) { - expect(container).toMatchInlineSnapshot(` -
- Loading with suspense -
-`); - } else { - expect(container).toMatchInlineSnapshot(` -
- Loading -
-`); - } - - await wait(); - - expect(container).toMatchInlineSnapshot(` -
- James -
-`); - }); - - it('not throws in react-dom/server', async () => { - const client = createMockClient(); - - await expect( - getMarkupFromTree({ - renderFunction: renderToString, - tree: , - }) - ).resolves.toMatchInlineSnapshot(`"James"`); - }); -}); diff --git a/src/__tests__/useApolloClient-test.tsx b/src/__tests__/useApolloClient-test.tsx index 30c60b3..a094fd5 100644 --- a/src/__tests__/useApolloClient-test.tsx +++ b/src/__tests__/useApolloClient-test.tsx @@ -1,5 +1,5 @@ +import { cleanup, render } from '@testing-library/react'; import React from 'react'; -import { cleanup, render } from 'react-testing-library'; import { ApolloProvider, useApolloClient } from '..'; import createClient from '../__testutils__/createClient'; diff --git a/src/__tests__/useMutation-test.tsx b/src/__tests__/useMutation-test.tsx index 0f99ce8..3ff3357 100644 --- a/src/__tests__/useMutation-test.tsx +++ b/src/__tests__/useMutation-test.tsx @@ -1,6 +1,6 @@ +import { cleanup, fireEvent, render } from '@testing-library/react'; import gql from 'graphql-tag'; import React from 'react'; -import { cleanup, fireEvent, render } from 'react-testing-library'; import { ApolloProvider, useMutation, useQuery } from '..'; import createClient from '../__testutils__/createClient'; diff --git a/src/__tests__/useQuery-test.tsx b/src/__tests__/useQuery-test.tsx index 5163cb3..2e78449 100644 --- a/src/__tests__/useQuery-test.tsx +++ b/src/__tests__/useQuery-test.tsx @@ -1,20 +1,20 @@ +import { cleanup, render } from '@testing-library/react'; import { ApolloClient } from 'apollo-client'; import { DocumentNode } from 'apollo-link'; import { MockedResponse } from 'apollo-link-mock'; -import gql from 'graphql-tag'; -import { withProfiler } from 'jest-react-profiler'; -import React, { Suspense, SuspenseProps } from 'react'; -import { cleanup, render } from 'react-testing-library'; - import { GraphQLError } from 'graphql'; +import gql from 'graphql-tag'; +import React from 'react'; import { ApolloProvider, QueryHookOptions, useQuery } from '..'; import createClient from '../__testutils__/createClient'; import { SAMPLE_TASKS } from '../__testutils__/data'; -import noop from '../__testutils__/noop'; -import wait from '../__testutils__/wait'; jest.mock('../internal/actHack'); +function wait(): Promise { + return new Promise(resolve => setTimeout(resolve, 0)); +} + const TASKS_QUERY = gql` query TasksQuery { tasks { @@ -51,6 +51,69 @@ const FILTERED_TASKS_QUERY = gql` } `; +const LOADING_SNAPSHOT = ` +
+ Loading +
+`; + +const TASKS_SNAPSHOT = ` +
+
    +
  • + Learn GraphQL +
  • +
  • + Learn React +
  • +
  • + Learn Apollo +
  • +
+
+`; + +const FILTERED_COMPLETED_SNAPSHOT = ` +
+
    +
  • + Learn GraphQL +
  • +
+
+`; + +const FILTERED_INCOMPLETE_SNAPSHOT = ` +
+
    +
  • + Learn React +
  • +
  • + Learn Apollo +
  • +
+
+`; + +const SKIPPED_DATA_SNAPSHOT = ` +
+ Skipped loading of data +
+`; + +const GRAPHQL_ERROR_SNAPSHOT = ` +
+ GraphQL error: Simulating GraphQL error +
+`; + +const NETWORK_ERROR_SNAPSHOT = ` +
+ Network error: Simulating network error +
+`; + const TASKS_MOCKS: MockedResponse[] = [ { request: { query: TASKS_QUERY, variables: {} }, @@ -119,7 +182,7 @@ function Tasks({ query, ...options }: TasksProps) { } if (loading) { - return <>Loading without suspense; + return <>Loading; } if (!data) { @@ -130,306 +193,73 @@ function Tasks({ query, ...options }: TasksProps) { } interface TasksWrapperProps extends TasksProps { - client?: ApolloClient; + client: ApolloClient; } -const SuspenseCompat = ({ children }: SuspenseProps) => <>{children}; - function TasksWrapper({ client, ...props }: TasksWrapperProps) { - const SuspenseComponent = props.suspend !== false ? Suspense : SuspenseCompat; - - const inner = ( - Loading with suspense}> + return ( + - + ); - - if (client) { - return {inner}; - } - - return inner; } afterEach(cleanup); -it('should return the query data', async () => { - const client = createMockClient(); +it('should run useQuery hook in absence of ApolloProvider', async () => { const { container } = render( - + ); - expect(container).toMatchInlineSnapshot(` -
- Loading without suspense -
-`); - - await wait(); - - expect(container).toMatchInlineSnapshot(` -
-
    -
  • - Learn GraphQL -
  • -
  • - Learn React -
  • -
  • - Learn Apollo -
  • -
-
-`); -}); - -it('should accept a client option', async () => { - const client = createMockClient(); - const { container } = render( - - ); - - expect(container).toMatchInlineSnapshot(` -
- Loading without suspense -
-`); + expect(container).toMatchInlineSnapshot(LOADING_SNAPSHOT); await wait(); - expect(container).toMatchInlineSnapshot(` -
-
    -
  • - Learn GraphQL -
  • -
  • - Learn React -
  • -
  • - Learn Apollo -
  • -
-
-`); + expect(container).toMatchInlineSnapshot(TASKS_SNAPSHOT); }); -it('should work with suspense enabled', async () => { +it('should return the query data', async () => { const client = createMockClient(); const { container } = render( - - ); - - expect(container).toMatchInlineSnapshot(` -
- Loading with suspense -
-`); - - await wait(); - - expect(container).toMatchInlineSnapshot(` -
-
    -
  • - Learn GraphQL -
  • -
  • - Learn React -
  • -
  • - Learn Apollo -
  • -
-
-`); -}); - -it.each([false, true])( - 'should support query variables with with "suspend: %s"', - async suspend => { - const client = createMockClient(); - const { container } = render( - - ); - - if (suspend) { - expect(container).toMatchInlineSnapshot(` -
- Loading with suspense -
-`); - } else { - expect(container).toMatchInlineSnapshot(` -
- Loading without suspense -
-`); - } - - await wait(); - - expect(container).toMatchInlineSnapshot(` -
-
    -
  • - Learn GraphQL -
  • -
-
-`); - } -); - -it('should support updating query variables without suspense', async () => { - const client = createMockClient(); - const { container, rerender } = render( - - ); - - expect(container).toMatchInlineSnapshot(` -
- Loading without suspense -
-`); - - await wait(); - - expect(container).toMatchInlineSnapshot(` -
-
    -
  • - Learn GraphQL -
  • -
-
-`); - - rerender( - + ); - expect(container).toMatchInlineSnapshot(` -
- Loading without suspense -
-`); + expect(container).toMatchInlineSnapshot(LOADING_SNAPSHOT); await wait(); - expect(container).toMatchInlineSnapshot(` -
-
    -
  • - Learn React -
  • -
  • - Learn Apollo -
  • -
-
-`); - - rerender( - - ); - - expect(container).toMatchInlineSnapshot(` -
-
    -
  • - Learn GraphQL -
  • -
-
-`); + expect(container).toMatchInlineSnapshot(TASKS_SNAPSHOT); }); -it('should support updating query variables with suspense', async () => { +it('should support updating query variables', async () => { const client = createMockClient(); const { container, rerender } = render( ); - expect(container).toMatchInlineSnapshot(` -
- Loading with suspense -
-`); + expect(container).toMatchInlineSnapshot(LOADING_SNAPSHOT); await wait(); - expect(container).toMatchInlineSnapshot(` -
-
    -
  • - Learn GraphQL -
  • -
-
-`); + expect(container).toMatchInlineSnapshot(FILTERED_COMPLETED_SNAPSHOT); rerender( ); - expect(container).toMatchInlineSnapshot(` -
-
    -
  • - Learn GraphQL -
  • -
- Loading with suspense -
-`); + expect(container).toMatchInlineSnapshot(LOADING_SNAPSHOT); await wait(); - expect(container).toMatchInlineSnapshot(` -
-
    -
  • - Learn React -
  • -
  • - Learn Apollo -
  • -
-
-`); + expect(container).toMatchInlineSnapshot(FILTERED_INCOMPLETE_SNAPSHOT); rerender( { /> ); - expect(container).toMatchInlineSnapshot(` -
-
    -
  • - Learn GraphQL -
  • -
-
-`); + expect(container).toMatchInlineSnapshot(FILTERED_COMPLETED_SNAPSHOT); }); -it("shouldn't suspend if the data is already cached", async () => { - const client = createMockClient(); - const { container, rerender } = render( - - ); - - await wait(); - - rerender( - - ); - - await wait(); - - rerender( - - ); - - expect(container).toMatchInlineSnapshot(` -
-
    -
  • - Learn GraphQL -
  • -
-
-`); -}); - -it("shouldn't allow a query with non-standard fetch policy with suspense", async () => { - const client = createMockClient(); - const consoleErrorMock = jest - .spyOn(console, 'error') - .mockImplementation(noop); - - expect(() => - render( - - ) - ).toThrowErrorMatchingInlineSnapshot( - `"Fetch policy cache-and-network is not supported without 'suspend: false'"` - ); - - expect(consoleErrorMock).toBeCalled(); - - consoleErrorMock.mockRestore(); -}); - -it('should allow a query with non-standard fetch policy without suspense', async () => { +it('should allow a query with non-standard fetch policy', async () => { const client = createMockClient(); const { container } = render( ); - expect(container).toMatchInlineSnapshot(` -
- Loading without suspense -
-`); - - await wait(); - - expect(container).toMatchInlineSnapshot(` -
-
    -
  • - Learn GraphQL -
  • -
  • - Learn React -
  • -
  • - Learn Apollo -
  • -
-
-`); -}); - -it("shouldn't make obsolete renders in suspense mode", async () => { - const client = createMockClient(); - const TasksWrapperWithProfiler = withProfiler(TasksWrapper); - - const { container, rerender } = render( - - ); - - expect(container).toMatchInlineSnapshot(` -
- Loading with suspense -
-`); - - expect(TasksWrapperWithProfiler).toHaveCommittedTimes(1); - - await wait(); - - expect(container).toMatchInlineSnapshot(` -
-
    -
  • - Learn GraphQL -
  • -
-
-`); - - // TODO: Find out why. - expect(TasksWrapperWithProfiler).toHaveCommittedTimes(2); - - rerender( - - ); - - expect(container).toMatchInlineSnapshot(` -
-
    -
  • - Learn GraphQL -
  • -
- Loading with suspense -
-`); - - await wait(); - - expect(container).toMatchInlineSnapshot(` -
-
    -
  • - Learn React -
  • -
  • - Learn Apollo -
  • -
-
-`); - - expect(TasksWrapperWithProfiler).toHaveCommittedTimes( - 3 // TODO: Figure out why. - ); - - rerender( - - ); - - expect(TasksWrapperWithProfiler).toHaveCommittedTimes(1); - - expect(container).toMatchInlineSnapshot(` -
-
    -
  • - Learn GraphQL -
  • -
-
-`); + expect(container).toMatchInlineSnapshot(LOADING_SNAPSHOT); await wait(); - expect(TasksWrapperWithProfiler).toHaveCommittedTimes(1); + expect(container).toMatchInlineSnapshot(TASKS_SNAPSHOT); }); -it('skips query in suspense mode', async () => { +it('skips query', async () => { const client = createMockClient(); const { container } = render( ); - expect(container).toMatchInlineSnapshot(` -
- Skipped loading of data -
-`); - - await wait(); - - expect(container).toMatchInlineSnapshot(` -
- Skipped loading of data -
-`); -}); - -it('skips query in non-suspense mode', async () => { - const client = createMockClient(); - const { container } = render( - - ); - - expect(container).toMatchInlineSnapshot(` -
- Skipped loading of data -
-`); - - await wait(); - - expect(container).toMatchInlineSnapshot(` -
- Skipped loading of data -
-`); -}); - -it('starts skipped query in suspense mode', async () => { - const client = createMockClient(); - const { rerender, container } = render( - - ); - - expect(container).toMatchInlineSnapshot(` -
- Skipped loading of data -
-`); - - await wait(); - - expect(container).toMatchInlineSnapshot(` -
- Skipped loading of data -
-`); - - rerender( - - ); - - expect(container).toMatchInlineSnapshot(` -
- - Loading with suspense -
-`); - - await wait(); - - expect(container).toMatchInlineSnapshot(` -
-
    -
  • - Learn GraphQL -
  • -
  • - Learn React -
  • -
  • - Learn Apollo -
  • -
-
-`); -}); - -it('starts skipped query in non-suspense mode', async () => { - const client = createMockClient(); - const { rerender, container } = render( - - ); - - expect(container).toMatchInlineSnapshot(` -
- Skipped loading of data -
-`); - - await wait(); - - expect(container).toMatchInlineSnapshot(` -
- Skipped loading of data -
-`); - - rerender( - - ); - - expect(container).toMatchInlineSnapshot(` -
- Loading without suspense -
-`); - - await wait(); - - expect(container).toMatchInlineSnapshot(` -
-
    -
  • - Learn GraphQL -
  • -
  • - Learn React -
  • -
  • - Learn Apollo -
  • -
-
-`); -}); - -it('handles network error in suspense mode', async () => { - const client = createMockClient(); - const { container } = render( - - ); - - expect(container).toMatchInlineSnapshot(` -
- Loading with suspense -
-`); + expect(container).toMatchInlineSnapshot(SKIPPED_DATA_SNAPSHOT); await wait(); - expect(container).toMatchInlineSnapshot(` -
- Network error: Simulating network error -
-`); + expect(container).toMatchInlineSnapshot(SKIPPED_DATA_SNAPSHOT); }); -it('handles network error in non-suspense mode', async () => { +it('handles network error', async () => { const client = createMockClient(); const { container } = render( - + ); - expect(container).toMatchInlineSnapshot(` -
- Loading without suspense -
-`); - await wait(); - - expect(container).toMatchInlineSnapshot(` -
- Network error: Simulating network error -
-`); -}); - -it('handles GraphQL error in suspense mode', async () => { - const client = createMockClient(); - const { container } = render( - - ); - - expect(container).toMatchInlineSnapshot(` -
- Loading with suspense -
-`); + expect(container).toMatchInlineSnapshot(LOADING_SNAPSHOT); await wait(); - expect(container).toMatchInlineSnapshot(` -
- GraphQL error: Simulating GraphQL error -
-`); + expect(container).toMatchInlineSnapshot(NETWORK_ERROR_SNAPSHOT); }); -it('handles GraphQL error in non-suspense mode', async () => { +it('handles GraphQL error', async () => { const client = createMockClient(); const { container } = render( - + ); - expect(container).toMatchInlineSnapshot(` -
- Loading without suspense -
-`); + expect(container).toMatchInlineSnapshot(LOADING_SNAPSHOT); await wait(); - expect(container).toMatchInlineSnapshot(` -
- GraphQL error: Simulating GraphQL error -
-`); + expect(container).toMatchInlineSnapshot(GRAPHQL_ERROR_SNAPSHOT); }); diff --git a/src/__tests__/useSubscription-test.tsx b/src/__tests__/useSubscription-test.tsx index e1a0b42..0bcb4a0 100644 --- a/src/__tests__/useSubscription-test.tsx +++ b/src/__tests__/useSubscription-test.tsx @@ -1,9 +1,9 @@ +import { act, cleanup, render } from '@testing-library/react'; import { ApolloClient } from 'apollo-client'; import { Operation } from 'apollo-link'; import { MockSubscriptionLink } from 'apollo-link-mock'; import gql from 'graphql-tag'; import React from 'react'; -import { act, cleanup, render } from 'react-testing-library'; import { ApolloProvider, useSubscription } from '..'; import createClient from '../__testutils__/createClient'; diff --git a/src/index.ts b/src/index.ts index 8cde75c..7a3c8af 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,5 @@ export * from './useQuery'; export * from './useMutation'; export * from './ApolloContext'; -export * from './SuspenseSSR'; export * from './getMarkupFromTree'; export * from './useSubscription'; diff --git a/src/internal/__mocks__/actHack.ts b/src/internal/__mocks__/actHack.ts index c60057c..3d3e93b 100644 --- a/src/internal/__mocks__/actHack.ts +++ b/src/internal/__mocks__/actHack.ts @@ -1,3 +1,3 @@ -import { act } from 'react-testing-library'; +import { act } from '@testing-library/react'; export default act; diff --git a/src/queryCache.ts b/src/queryCache.ts index 85e5d33..c141ae4 100644 --- a/src/queryCache.ts +++ b/src/queryCache.ts @@ -2,6 +2,7 @@ import ApolloClient, { ObservableQuery, WatchQueryOptions, } from 'apollo-client'; +import { createHash } from 'crypto'; import { print } from 'graphql/language/printer'; import { objToKey } from './utils'; @@ -47,5 +48,8 @@ function getCacheKey({ query, ...options }: WatchQueryOptions): string { - return `${print(query)}@@${objToKey(options)}`; + const cacheKey = createHash('sha1') + .update(`${print(query)}@@${objToKey(options)}`) + .digest('base64'); + return cacheKey; } diff --git a/src/useMutation.ts b/src/useMutation.ts index 23b3b5e..24fa335 100644 --- a/src/useMutation.ts +++ b/src/useMutation.ts @@ -45,5 +45,8 @@ export function useMutation< ): MutationFn { const client = useApolloClient(overrideClient); - return options => client.mutate({ mutation, ...baseOptions, ...options }); + return options => + client.mutate({ mutation, ...baseOptions, ...options }) as Promise< + FetchResult + >; } diff --git a/src/useQuery.ts b/src/useQuery.ts index 17a7c47..87bc53a 100644 --- a/src/useQuery.ts +++ b/src/useQuery.ts @@ -4,8 +4,6 @@ import ApolloClient, { ApolloQueryResult, FetchMoreOptions, FetchMoreQueryOptions, - FetchPolicy, - NetworkStatus, ObservableQuery, OperationVariables, QueryOptions, @@ -28,8 +26,6 @@ export interface QueryHookState 'error' | 'errors' | 'loading' | 'partial' > { data?: TData; - // networkStatus is undefined for skipped queries or the ones using suspense - networkStatus: NetworkStatus | undefined; } export interface QueryHookOptions @@ -66,7 +62,6 @@ export function useQuery< // Hook options ssr = true, skip = false, - suspend = false, // Watch options pollInterval, @@ -159,7 +154,6 @@ export function useQuery< data: undefined, error: undefined, loading: false, - networkStatus: undefined, }; } @@ -172,10 +166,7 @@ export function useQuery< : result.error, errors: result.errors, loading: result.loading, - // don't try to return `networkStatus` when suspense it's used - // because it's unreliable in that case - // https://github.com/trojanowski/react-apollo-hooks/pull/68 - networkStatus: suspend ? undefined : result.networkStatus, + networkStatus: result.networkStatus, partial: result.partial, }; }, @@ -191,7 +182,7 @@ export function useQuery< const invalidateCurrentResult = () => { // A hack to get rid React warnings during tests. The default // implementation of `actHack` just invokes the callback immediately. - // In tests, it's replaced with `act` from react-testing-library. + // In tests, it's replaced with `act` from @testing-library/react. // A better solution welcome. actHack(() => { setResponseId(x => x + 1); @@ -211,30 +202,10 @@ export function useQuery< [shouldSkip, observableQuery] ); - ensureSupportedFetchPolicy(suspend, fetchPolicy); - if (currentResult.partial) { - if (suspend) { - // throw a promise - use the react suspense to wait until the data is - // available - throw observableQuery.result(); - } - if (ssrInUse) { ssrManager!.register(observableQuery.result()); } } - return currentResult; } - -function ensureSupportedFetchPolicy( - suspend: boolean, - fetchPolicy?: FetchPolicy -) { - if (suspend && fetchPolicy && fetchPolicy !== 'cache-first') { - throw new Error( - `Fetch policy ${fetchPolicy} is not supported without 'suspend: false'` - ); - } -} diff --git a/tslint.json b/tslint.json index 267164b..297ba37 100644 --- a/tslint.json +++ b/tslint.json @@ -20,7 +20,7 @@ "apollo-link-mock", "apollo-cache-inmemory", "react-dom", - "react-testing-library" + "@testing-library/react" ] ], "no-submodule-imports": false, diff --git a/yarn.lock b/yarn.lock index 30dcdd3..6a24d4a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -648,12 +648,12 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-transform-typescript" "^7.1.0" -"@babel/runtime@^7.1.5", "@babel/runtime@^7.3.1": - version "7.3.1" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.1.tgz#574b03e8e8a9898eaf4a872a92ea20b7846f6f2a" - integrity sha512-7jGW8ppV0ant637pIqAcFfQDDH1orEPGJb8aXfUozuCU3QqX7rX4DA8iwrbPrR1hcH0FTTHz47yQnk+bl5xHQA== +"@babel/runtime@^7.4.5": + version "7.4.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.5.tgz#582bb531f5f9dc67d2fcb682979894f75e253f12" + integrity sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ== dependencies: - regenerator-runtime "^0.12.0" + regenerator-runtime "^0.13.2" "@babel/template@^7.1.0", "@babel/template@^7.1.2": version "7.1.2" @@ -702,6 +702,15 @@ log-update "^2.3.0" strip-ansi "^3.0.1" +"@jest/types@^24.8.0": + version "24.8.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.8.0.tgz#f31e25948c58f0abd8c845ae26fcea1491dea7ad" + integrity sha512-g17UxVr2YfBtaMUxn9u/4+siG1ptg9IGYAYwvpwn61nBg779RXnjE/m7CxYcIzEt0AbHZZAHSEZNhkE2WxURVg== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^1.1.1" + "@types/yargs" "^12.0.9" + "@samverschueren/stream-to-observable@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" @@ -714,6 +723,25 @@ resolved "https://registry.yarnpkg.com/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz#8013f2af54a2b7d735f71560ff360d3a8176a87b" integrity sha512-vTCdPp/T/Q3oSqwHmZ5Kpa9oI7iLtGl3RQaA/NyLHikvcrPxACkkKVr/XzkSPJWXHRhKGzVvb0urJsbMlRxi1Q== +"@testing-library/dom@^5.0.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-5.2.0.tgz#450bd09f93ddd8865eec98fc5e81fceb2bccdf9c" + integrity sha512-nFaZes/bzDfMqwZpQXdiPyj3WXU16FYf5k5NCFu/qJM4JdRJLHEtSRYtrETmk7nCf+qLVoHCqRduGi/4KE83Gw== + dependencies: + "@babel/runtime" "^7.4.5" + "@sheerun/mutationobserver-shim" "^0.3.2" + aria-query "3.0.0" + pretty-format "^24.8.0" + wait-for-expect "^1.2.0" + +"@testing-library/react@^8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-8.0.1.tgz#91c254adf855b13de50020613cb5d3915f9f7875" + integrity sha512-N/1pJfhEnNYkGyxuw4xbp03evaS0z/CT8o0QgTfJqGlukAcU15xf9uU1w03NHKZJcU69nOCBAoAkXHtHzYwMbg== + dependencies: + "@babel/runtime" "^7.4.5" + "@testing-library/dom" "^5.0.0" + "@types/async@2.0.50": version "2.0.50" resolved "https://registry.yarnpkg.com/@types/async/-/async-2.0.50.tgz#117540e026d64e1846093abbd5adc7e27fda7bcb" @@ -724,6 +752,26 @@ resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-14.0.3.tgz#389e2e5b83ecdb376d9f98fae2094297bc112c1c" integrity sha512-TcFkpEjcQK7w8OcrQcd7iIBPjU0rdyi3ldj6d0iJ4PPSzbWqPBvXj9KSwO14hTOX2dm9RoiH7VuxksJLNYdXUQ== +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff" + integrity sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg== + +"@types/istanbul-lib-report@*": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz#e5471e7fa33c61358dd38426189c037a58433b8c" + integrity sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz#7a8cbf6a406f36c8add871625b278eaf0b0d255a" + integrity sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA== + dependencies: + "@types/istanbul-lib-coverage" "*" + "@types/istanbul-lib-report" "*" + "@types/jest@^23.3.10": version "23.3.10" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.3.10.tgz#4897974cc317bf99d4fe6af1efa15957fa9c94de" @@ -734,6 +782,11 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.119.tgz#be847e5f4bc3e35e46d041c394ead8b603ad8b39" integrity sha512-Z3TNyBL8Vd/M9D9Ms2S3LmFq2sSMzahodD6rCS9V2N44HUMINb75jNkSuwAx7eo2ufqTdfOdtGQpNbieUjPQmw== +"@types/node@^12.0.8": + version "12.0.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.0.8.tgz#551466be11b2adc3f3d47156758f610bd9f6b1d8" + integrity sha512-b8bbUOTwzIY3V5vDTY1fIJ+ePKDUBqt2hC2woVGotdQQhG/2Sh62HOKHrT7ab+VerXAcPyAiTEipPu/FsreUtg== + "@types/prop-types@*": version "15.5.6" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.6.tgz#9c03d3fed70a8d517c191b7734da2879b50ca26c" @@ -754,6 +807,11 @@ "@types/prop-types" "*" csstype "^2.2.0" +"@types/yargs@^12.0.9": + version "12.0.12" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.12.tgz#45dd1d0638e8c8f153e87d296907659296873916" + integrity sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw== + "@types/zen-observable@^0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.0.tgz#8b63ab7f1aa5321248aad5ac890a485656dcea4d" @@ -825,6 +883,11 @@ ansi-regex@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= +ansi-regex@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -939,6 +1002,14 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +aria-query@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc" + integrity sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w= + dependencies: + ast-types-flow "0.0.7" + commander "^2.11.0" + arr-diff@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" @@ -1020,6 +1091,11 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= +ast-types-flow@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" + integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= + astral-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" @@ -1537,6 +1613,11 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" +commander@^2.11.0: + version "2.20.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" + integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== + commander@^2.12.1, commander@^2.14.1, commander@^2.8.1, commander@^2.9.0: version "2.19.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" @@ -2006,16 +2087,6 @@ diff@^3.2.0: resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== -dom-testing-library@^3.13.1: - version "3.16.1" - resolved "https://registry.yarnpkg.com/dom-testing-library/-/dom-testing-library-3.16.1.tgz#9911d775adba1cf5b68ec30a02bbf6b2989727f9" - integrity sha512-dYVeCXo7cBviHP7c+bRlXm0r0HXSOcUeMt6kKqnpCSKhyPbRFas1IKhjB/APP4yhBDwn9XECIC84ZlgTEiCm4A== - dependencies: - "@babel/runtime" "^7.1.5" - "@sheerun/mutationobserver-shim" "^0.3.2" - pretty-format "^23.6.0" - wait-for-expect "^1.1.0" - domexception@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" @@ -4693,6 +4764,16 @@ pretty-format@^23.6.0: ansi-regex "^3.0.0" ansi-styles "^3.2.0" +pretty-format@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.8.0.tgz#8dae7044f58db7cb8be245383b565a963e3c27f2" + integrity sha512-P952T7dkrDEplsR+TuY7q3VXDae5Sr7zmQb12JU/NDQa/3CH7/QW0yvqLcGN6jL+zQFKaoJcPc+yJxMTGmosqw== + dependencies: + "@jest/types" "^24.8.0" + ansi-regex "^4.0.0" + ansi-styles "^3.2.0" + react-is "^16.8.4" + private@^0.1.6, private@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" @@ -4791,13 +4872,10 @@ react-dom@16.8.1: prop-types "^15.6.2" scheduler "^0.13.1" -react-testing-library@^5.5.3: - version "5.5.3" - resolved "https://registry.yarnpkg.com/react-testing-library/-/react-testing-library-5.5.3.tgz#cfd32da95b29e475003df4f66bd96e34eb5624bd" - integrity sha512-67jMsSJHbbm9M0NWvEzjNikDAKRdxivhP6vnpa9xPg/fYh19zkE4rMsFh5YWLpyoomm+e49fg+ubcXaEBYartA== - dependencies: - "@babel/runtime" "^7.3.1" - dom-testing-library "^3.13.1" +react-is@^16.8.4: + version "16.8.6" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" + integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA== react@16.8.1: version "16.8.1" @@ -4931,10 +5009,10 @@ regenerator-runtime@^0.11.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== -regenerator-runtime@^0.12.0: - version "0.12.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de" - integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg== +regenerator-runtime@^0.13.2: + version "0.13.2" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447" + integrity sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA== regenerator-transform@^0.13.3: version "0.13.3" @@ -5856,10 +5934,10 @@ w3c-hr-time@^1.0.1: dependencies: browser-process-hrtime "^0.1.2" -wait-for-expect@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/wait-for-expect/-/wait-for-expect-1.1.0.tgz#6607375c3f79d32add35cd2c87ce13f351a3d453" - integrity sha512-vQDokqxyMyknfX3luCDn16bSaRcOyH6gGuUXMIbxBLeTo6nWuEWYqMTT9a+44FmW8c2m6TRWBdNvBBjA1hwEKg== +wait-for-expect@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/wait-for-expect/-/wait-for-expect-1.2.0.tgz#fdab6a26e87d2039101db88bff3d8158e5c3e13f" + integrity sha512-EJhKpA+5UHixduMBEGhTFuLuVgQBKWxkFbefOdj2bbk2/OpA5Opsc4aUTGmF+qJ+v3kTGxDRNYwKaT4j6g5n8Q== walker@~1.0.5: version "1.0.7"