Skip to content

Commit

Permalink
Update usePaginationFragment types to use RefetchableFragment type in…
Browse files Browse the repository at this point in the history
…stead of GraphQLTaggedNode for fragment type.

Reviewed By: voideanvalue

Differential Revision: D42700520

fbshipit-source-id: a226630c4aa4f82c098ef63e31f25d8814c5972c
  • Loading branch information
alunyov authored and facebook-github-bot committed Jan 24, 2023
1 parent baaf96d commit 614c747
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,82 +14,81 @@ import type {
FetchFn,
NonNullableData,
NullableData,
QueryOperation,
QueryVariables,
QueryVariablesSubset,
} from './utils';
import type {IEnvironment, OperationType} from 'relay-runtime';
import type {IEnvironment, Variables} from 'relay-runtime';

import usePaginationFragment from '../usePaginationFragment';
import {
fragmentData,
fragmentInput,
refetchableFragmentInput,
keyAnotherNonNullable,
keyAnotherNullable,
keyNonNullable,
keyNullable,
} from './utils';

type ExpectedReturnType<
TQuery: OperationType,
TQueryVariables,
TRefetchVariables: Variables,
TLoadMoreVariables: Variables,
TFragmentData,
> = {
data: TFragmentData,
loadNext: LoadMoreFn<TQuery['variables']>,
loadPrevious: LoadMoreFn<TQuery['variables']>,
loadNext: LoadMoreFn<TLoadMoreVariables>,
loadPrevious: LoadMoreFn<TLoadMoreVariables>,
hasNext: boolean,
hasPrevious: boolean,
isLoadingNext: boolean,
isLoadingPrevious: boolean,
refetch: FetchFn<TQueryVariables>,
refetch: FetchFn<TRefetchVariables>,
};

/* eslint-disable react-hooks/rules-of-hooks */

// Nullability of returned data type is correct
(usePaginationFragment<QueryOperation, _>(
fragmentInput,
(usePaginationFragment(
refetchableFragmentInput,
keyNonNullable,
): ExpectedReturnType<QueryOperation, QueryVariablesSubset, NonNullableData>);
): ExpectedReturnType<QueryVariablesSubset, QueryVariables, NonNullableData>);

(usePaginationFragment<QueryOperation, _>(
fragmentInput,
(usePaginationFragment(
refetchableFragmentInput,
keyNullable,
): ExpectedReturnType<QueryOperation, QueryVariables, NullableData>);
): ExpectedReturnType<QueryVariables, QueryVariables, NullableData>);

// $FlowExpectedError: can't cast nullable to non-nullable
(usePaginationFragment<QueryOperation, _>(
fragmentInput,
(usePaginationFragment(
refetchableFragmentInput,
keyNullable,
): ExpectedReturnType<QueryOperation, QueryVariables, NonNullableData>);
): ExpectedReturnType<QueryVariables, QueryVariables, NonNullableData>);

// $FlowExpectedError: actual type of returned data is correct
(usePaginationFragment<QueryOperation, _>(
fragmentInput,
(usePaginationFragment(
refetchableFragmentInput,
keyAnotherNonNullable,
): ExpectedReturnType<QueryOperation, QueryVariablesSubset, NonNullableData>);
): ExpectedReturnType<QueryVariables, QueryVariablesSubset, NonNullableData>);
// $FlowExpectedError
(usePaginationFragment<QueryOperation, _>(
fragmentInput,
(usePaginationFragment(
refetchableFragmentInput,
keyAnotherNullable,
): ExpectedReturnType<QueryOperation, QueryVariables, NullableData>);
): ExpectedReturnType<QueryVariables, QueryVariables, NonNullableData>);

// $FlowExpectedError: Key should not be a user provided object
usePaginationFragment<QueryOperation, _>(fragmentInput, {abc: 123});
usePaginationFragment(refetchableFragmentInput, {abc: 123});

// $FlowExpectedError: Key should not be an empty object
usePaginationFragment<QueryOperation, _>(fragmentInput, {});
usePaginationFragment(refetchableFragmentInput, {});

// $FlowExpectedError: Key should be the `<name>$key` type from generated flow
usePaginationFragment<QueryOperation, _>(fragmentInput, fragmentData);
usePaginationFragment(refetchableFragmentInput, fragmentData);

// Refetch function options:
declare var variables: QueryVariables;
declare var environment: IEnvironment;

const {refetch} = usePaginationFragment<QueryOperation, _>(
fragmentInput,
const {refetch} = usePaginationFragment(
refetchableFragmentInput,
keyNonNullable,
);
// $FlowExpectedError: internal option
Expand All @@ -106,17 +105,17 @@ refetch(variables, {
declare var extraVariables: {nickname: string};
declare var invalidVariables: {foo: string};

const {loadNext} = usePaginationFragment<QueryOperation, _>(
fragmentInput,
const {loadNext} = usePaginationFragment(
refetchableFragmentInput,
keyNonNullable,
);
// Accepts extraVariables
loadNext(10, {
UNSTABLE_extraVariables: extraVariables,
});

// $FlowExpectedError: doesn't accept variables not available in the Flow type
loadNext(10, {
// $FlowExpectedError: doesn't accept variables not available in the Flow type
UNSTABLE_extraVariables: invalidVariables,
});

Expand Down
114 changes: 59 additions & 55 deletions packages/react-relay/relay-hooks/usePaginationFragment.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,11 @@
import type {Options} from './useRefetchableFragmentNode';

import type {LoadMoreFn, UseLoadMoreFunctionArgs} from './useLoadMoreFunction';
import type {RefetchFnDynamic} from './useRefetchableFragmentNode';
import type {
FragmentType,
GraphQLResponse,
GraphQLTaggedNode,
Observer,
OperationType,
RefetchableFragment,
Variables,
} from 'relay-runtime';

Expand All @@ -34,44 +32,30 @@ const {
getFragmentIdentifier,
getPaginationMetadata,
} = require('relay-runtime');

export type ReturnType<TQuery: OperationType, TKey> = {
// NOTE: This $Call ensures that the type of the returned data is either:
// - nullable if the provided ref type is nullable
// - non-nullable if the provided ref type is non-nullable
// prettier-ignore
data: $Call<
& (<TFragmentData>( { +$data?: TFragmentData, ... }) => TFragmentData)
& (<TFragmentData>(?{ +$data?: TFragmentData, ... }) => ?TFragmentData),
TKey,
>,
loadNext: LoadMoreFn<TQuery['variables']>,
loadPrevious: LoadMoreFn<TQuery['variables']>,
hasNext: boolean,
hasPrevious: boolean,
isLoadingNext: boolean,
isLoadingPrevious: boolean,
refetch: RefetchFnDynamic<TQuery, TKey>,
};
import type {RefetchFn} from './useRefetchableFragment';

// This separate type export is only needed as long as we are injecting
// a separate hooks implementation in ./HooksImplementation -- it can
// be removed after we stop doing that.
export type UsePaginationFragmentType = <
TQuery: OperationType,
TKey: ?{+$data?: mixed, +$fragmentSpreads: FragmentType, ...},
TFragmentType: FragmentType,
TVariables: Variables,
TData,
TKey: ?{+$fragmentSpreads: TFragmentType, ...},
>(
fragmentInput: GraphQLTaggedNode,
fragmentInput: RefetchableFragment<TFragmentType, TData, TVariables>,
parentFragmentRef: TKey,
) => ReturnType<TQuery, TKey>;
) => ReturnType<TVariables, TData, TKey>;

function usePaginationFragment_LEGACY<
TQuery: OperationType,
TKey: ?{+$data?: mixed, +$fragmentSpreads: FragmentType, ...},
TFragmentType: FragmentType,
TVariables: Variables,
TData,
TKey: ?{+$fragmentSpreads: TFragmentType, ...},
>(
fragmentInput: GraphQLTaggedNode,
fragmentInput: RefetchableFragment<TFragmentType, TData, TVariables>,
parentFragmentRef: TKey,
): ReturnType<TQuery, TKey> {
): ReturnType<TVariables, TData, TKey> {
const fragmentNode = getFragment(fragmentInput);
useStaticFragmentNodeWarning(
fragmentNode,
Expand All @@ -87,14 +71,14 @@ function usePaginationFragment_LEGACY<
} = getPaginationMetadata(fragmentNode, componentDisplayName);

const {fragmentData, fragmentRef, refetch} = useRefetchableFragmentNode<
TQuery,
TKey,
$FlowFixMe,
$FlowFixMe,
>(fragmentNode, parentFragmentRef, componentDisplayName);
const fragmentIdentifier = getFragmentIdentifier(fragmentNode, fragmentRef);

// Backward pagination
const [loadPrevious, hasPrevious, isLoadingPrevious, disposeFetchPrevious] =
useLoadMore<TQuery['variables']>({
useLoadMore<TVariables>({
componentDisplayName,
connectionPathInFragmentData,
direction: 'backward',
Expand All @@ -108,23 +92,22 @@ function usePaginationFragment_LEGACY<
});

// Forward pagination
const [loadNext, hasNext, isLoadingNext, disposeFetchNext] = useLoadMore<
TQuery['variables'],
>({
componentDisplayName,
connectionPathInFragmentData,
direction: 'forward',
fragmentData,
fragmentIdentifier,
fragmentNode,
fragmentRef,
identifierField,
paginationMetadata,
paginationRequest,
});
const [loadNext, hasNext, isLoadingNext, disposeFetchNext] =
useLoadMore<TVariables>({
componentDisplayName,
connectionPathInFragmentData,
direction: 'forward',
fragmentData,
fragmentIdentifier,
fragmentNode,
fragmentRef,
identifierField,
paginationMetadata,
paginationRequest,
});

const refetchPagination: RefetchFnDynamic<TQuery, TKey> = useCallback(
(variables: TQuery['variables'], options: void | Options) => {
const refetchPagination: RefetchFn<TVariables, TKey> = useCallback(
(variables: TVariables, options: void | Options) => {
disposeFetchNext();
disposeFetchPrevious();
return refetch(variables, {...options, __environment: undefined});
Expand All @@ -144,7 +127,7 @@ function usePaginationFragment_LEGACY<
});
}
return {
data: fragmentData,
data: (fragmentData: $FlowFixMe),
loadNext,
loadPrevious,
hasNext,
Expand Down Expand Up @@ -180,16 +163,37 @@ function useLoadMore<TVariables: Variables>(
return [loadMore, hasMore, isLoadingMore, disposeFetch];
}

export type ReturnType<TVariables, TData, TKey> = {
// NOTE: This $Call ensures that the type of the returned data is either:
// - nullable if the provided ref type is nullable
// - non-nullable if the provided ref type is non-nullable
// prettier-ignore
data: $Call<
& (<TFragmentType>( { +$fragmentSpreads: TFragmentType, ... }) => TData)
& (<TFragmentType>(?{ +$fragmentSpreads: TFragmentType, ... }) => ?TData),
TKey,
>,
loadNext: LoadMoreFn<TVariables>,
loadPrevious: LoadMoreFn<TVariables>,
hasNext: boolean,
hasPrevious: boolean,
isLoadingNext: boolean,
isLoadingPrevious: boolean,
refetch: RefetchFn<TVariables, TKey>,
};

function usePaginationFragment<
TQuery: OperationType,
TKey: ?{+$data?: mixed, +$fragmentSpreads: FragmentType, ...},
TFragmentType: FragmentType,
TVariables: Variables,
TData,
TKey: ?{+$fragmentSpreads: TFragmentType, ...},
>(
fragmentInput: GraphQLTaggedNode,
fragmentInput: RefetchableFragment<TFragmentType, TData, TVariables>,
parentFragmentRef: TKey,
): ReturnType<TQuery, TKey> {
): ReturnType<TVariables, TData, TKey> {
const impl = HooksImplementation.get();
if (impl) {
return impl.usePaginationFragment<TQuery, TKey>(
return impl.usePaginationFragment<TFragmentType, TVariables, TData, TKey>(
fragmentInput,
parentFragmentRef,
);
Expand Down

0 comments on commit 614c747

Please sign in to comment.