Merge pull request #28379 from alloy/relay-queries-and-mutations

[react-relay] Add generics to QueryRenderer and commitMutation.
This commit is contained in:
Eloy Durán
2018-08-26 15:18:23 +02:00
committed by GitHub
3 changed files with 89 additions and 39 deletions

View File

@@ -7,7 +7,7 @@
// Cameron Knight <https://github.com/ckknight>
// Kaare Hoff Skovgaard <https://github.com/kastermester>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.8
// TypeScript Version: 2.9
// Prettified with:
// $ prettier --parser typescript --tab-width 4 --semi --trailing-comma es5 --write --print-width 120 \
@@ -88,24 +88,27 @@ export const graphql: GraphqlInterface;
// ~~~~~~~~~~~~~~~~~~~~~
// ReactRelayQueryRenderer
// ~~~~~~~~~~~~~~~~~~~~~
export interface QueryRendererProps {
export interface QueryRendererProps<T extends RelayRuntimeTypes.OperationBase = RelayRuntimeTypes.OperationDefaults> {
cacheConfig?: RelayRuntimeTypes.CacheConfig;
environment: RelayRuntimeTypes.Environment;
query?: RelayRuntimeTypes.GraphQLTaggedNode | null;
render(readyState: ReadyState): React.ReactElement<any> | undefined | null;
variables: RelayRuntimeTypes.Variables;
render(readyState: ReadyState<T["response"]>): React.ReactElement<any> | undefined | null;
variables: T["variables"];
rerunParamExperimental?: RelayRuntimeTypes.RerunParam;
}
export interface ReadyState {
export interface ReadyState<T extends RelayRuntimeTypes.Variables = RelayRuntimeTypes.Variables> {
error: Error | undefined | null;
props: { [propName: string]: any } | undefined | null;
props: T | undefined | null;
retry?(): void;
}
export interface QueryRendererState {
readyState: ReadyState;
}
export class ReactRelayQueryRenderer extends React.Component<QueryRendererProps, QueryRendererState> {}
export class QueryRenderer extends ReactRelayQueryRenderer {}
export class ReactRelayQueryRenderer<T extends RelayRuntimeTypes.OperationBase> extends React.Component<
QueryRendererProps<T>
> {}
export class QueryRenderer<
T extends RelayRuntimeTypes.OperationBase = RelayRuntimeTypes.OperationDefaults
> extends ReactRelayQueryRenderer<T> {}
// ~~~~~~~~~~~~~~~~~~~~~
// createFragmentContainer

View File

@@ -1,3 +1,5 @@
// tslint:disable:interface-over-type-literal
import * as React from "react";
import { Environment, Network, RecordSource, Store, ConnectionHandler, FragmentReference } from "relay-runtime";
@@ -28,8 +30,21 @@ const modernEnvironment = new Environment({ network, store });
// ~~~~~~~~~~~~~~~~~~~~~
// Modern QueryRenderer
// ~~~~~~~~~~~~~~~~~~~~~
// Artifact produced by relay-compiler-language-typescript
type MyQueryRendererVariables = {
pageID: string;
};
type MyQueryRendererResponse = {
name: string;
};
type MyQueryRenderer = {
variables: MyQueryRendererVariables;
response: MyQueryRendererResponse;
};
const MyQueryRenderer = (props: { name: string; show: boolean }) => (
<QueryRenderer
<QueryRenderer<MyQueryRenderer>
environment={modernEnvironment}
query={
props.show
@@ -82,7 +97,6 @@ type StoryLike = (storyID: string) => void;
// Artifact produced by relay-compiler-language-typescript
declare const _Story_story$ref: unique symbol;
type Story_story$ref = typeof _Story_story$ref;
// tslint:disable-next-line:interface-over-type-literal
type Story_story = {
readonly id: string;
readonly text: string;
@@ -173,7 +187,6 @@ declare const _FeedStories_feed$ref: unique symbol;
type FeedStories_feed$ref = typeof _FeedStories_feed$ref;
declare const _FeedStory_edges$ref: unique symbol;
type FeedStory_edges$ref = typeof _FeedStory_edges$ref;
// tslint:disable-next-line:interface-over-type-literal
type FeedStories_feed = {
readonly edges: ReadonlyArray<{
readonly node: {
@@ -184,7 +197,6 @@ type FeedStories_feed = {
}>;
readonly " $refType": FeedStories_feed$ref;
};
// tslint:disable-next-line:interface-over-type-literal
type FeedStory_edges = ReadonlyArray<{
readonly publishedAt: string;
readonly " $refType": FeedStory_edges$ref;
@@ -255,7 +267,6 @@ const Feed = (() => {
// Artifact produced by relay-compiler-language-typescript
declare const _UserFeed_user$ref: unique symbol;
type UserFeed_user$ref = typeof _UserFeed_user$ref;
// tslint:disable-next-line:interface-over-type-literal
type UserFeed_user = {
readonly feed: {
readonly pageInfo: {
@@ -372,7 +383,7 @@ export const mutation = graphql`
export const optimisticResponse = {
markReadNotification: {
notification: {
seenState: "SEEN",
seenState: "SEEN" as "SEEN",
},
},
};
@@ -408,26 +419,47 @@ export const configs = [
];
function markNotificationAsRead(source: string, storyID: string) {
const variables = {
input: {
source,
storyID,
},
// Artifact produced by relay-compiler-language-typescript
type MyMutationVariables = {
readonly input: {
readonly source: string;
readonly storyID: string;
};
};
type MyMutationResponse = {
readonly markReadNotification: {
readonly notification: {
readonly seenState: "SEEN" | "UNSEEN";
};
};
};
type MyMutation = {
readonly variables: MyMutationVariables;
readonly response: MyMutationResponse;
};
commitMutation(modernEnvironment, {
commitMutation<MyMutation>(modernEnvironment, {
configs,
mutation,
optimisticResponse,
variables,
variables: {
input: {
source,
storyID,
},
},
onCompleted: (response, errors) => {
console.log("Response received from server.");
if (errors) {
console.log(`Errors received from server: ${errors.map(error => error.message).join(", ")}`);
} else {
console.log(`Response received from server: ${response.markReadNotification.notification.seenState}`);
}
},
onError: err => console.error(err),
updater: (store, data) => {
const field = store.get(storyID);
if (field) {
field.setValue(data.story, "story");
const story = store.get(storyID);
if (story) {
story.setValue(data.markReadNotification.notification.seenState, "seenState");
}
},
});

View File

@@ -3,7 +3,7 @@
// Definitions by: Matt Martin <https://github.com/voxmatt>
// Eloy Durán <https://github.com/alloy>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
// TypeScript Version: 2.9
// Prettified with:
// $ prettier --parser typescript --tab-width 4 --semi --trailing-comma es5 --write --print-width 120 \
@@ -51,6 +51,15 @@ export type RequestNode = ConcreteRequest | ConcreteBatchRequest;
// tslint:disable-next-line:no-const-enum
export const enum FragmentReference {}
export interface OperationBase {
variables: object;
response: object;
}
export interface OperationDefaults {
variables: Variables;
response: Variables;
}
// ~~~~~~~~~~~~~~~~~~~~~
// RelayQL
// ~~~~~~~~~~~~~~~~~~~~~
@@ -140,11 +149,11 @@ export type StoreUpdater = (store: RecordSourceProxy) => void;
* order to easily access the root fields of a query/mutation as well as a
* second argument of the response object of the mutation.
*/
export type SelectorStoreUpdater = (
export type SelectorStoreUpdater<T = any> = (
store: RecordSourceSelectorProxy,
// Actually RelayCombinedEnvironmentTypes#SelectorData, but mixed is
// inconvenient to access deeply in product code.
data: any // FLOW FIXME
data: T
) => void;
/**
@@ -483,7 +492,9 @@ export interface CUnstableEnvironmentCore<TEnvironment, TFragment, TGraphQLTagge
operationVariables: Variables,
fragments: CFragmentMap<TFragment>,
props: Props
): { [key: string]: CSelector<TNode> | Array<CSelector<TNode>> | null | undefined };
): {
[key: string]: CSelector<TNode> | Array<CSelector<TNode>> | null | undefined;
};
/**
* Given a mapping of keys -> results and a mapping of keys -> fragments,
@@ -1005,18 +1016,22 @@ export type commitLocalUpdate = (environment: Environment, updater: StoreUpdater
// commitRelayModernMutation
// ~~~~~~~~~~~~~~~~~~~~~
// exposed through RelayModern, not Runtime directly
export interface MutationConfig<T> {
export interface MutationConfig<T extends OperationBase> {
configs?: RelayMutationConfig[];
mutation: GraphQLTaggedNode;
variables: Variables;
variables: T["variables"];
uploadables?: UploadableMap;
onCompleted?(response: T, errors: PayloadError[] | null | undefined): void;
onCompleted?(response: T["response"], errors: PayloadError[] | null | undefined): void;
onError?(error?: Error): void;
optimisticUpdater?: SelectorStoreUpdater;
optimisticResponse?: object;
updater?: SelectorStoreUpdater;
optimisticUpdater?: SelectorStoreUpdater<T["response"]>;
optimisticResponse?: T["response"];
updater?: SelectorStoreUpdater<T["response"]>;
}
export function commitRelayModernMutation(environment: Environment, config: MutationConfig<any>): Disposable;
export function commitRelayModernMutation<T extends OperationBase = OperationDefaults>(
environment: Environment,
// tslint:disable-next-line:no-unnecessary-generics
config: MutationConfig<T>
): Disposable;
// ~~~~~~~~~~~~~~~~~~~~~
// applyRelayModernOptimisticMutation