mirror of
https://github.com/zhigang1992/DefinitelyTyped.git
synced 2026-06-03 06:27:46 +08:00
Merge pull request #23460 from alloy/relay-better-relay-provided-props-2
[Relay] Various improvements
This commit is contained in:
2
types/react-relay/classic.d.ts
vendored
2
types/react-relay/classic.d.ts
vendored
@@ -117,7 +117,7 @@ export class DefaultNetworkLayer implements RelayNetworkLayer {
|
||||
}
|
||||
|
||||
export function createContainer<T>(
|
||||
component: React.ComponentClass<T> | React.StatelessComponent<T>,
|
||||
component: React.ComponentType<T>,
|
||||
params?: CreateContainerOpts
|
||||
): RelayContainerClass<T>;
|
||||
export function injectNetworkLayer(networkLayer: RelayNetworkLayer): any;
|
||||
|
||||
18
types/react-relay/compat.d.ts
vendored
18
types/react-relay/compat.d.ts
vendored
@@ -1,21 +1,16 @@
|
||||
export {
|
||||
QueryRenderer,
|
||||
fetchQuery,
|
||||
graphql,
|
||||
} from "./index";
|
||||
export { QueryRenderer, fetchQuery, graphql } from "./index";
|
||||
import {
|
||||
ConnectionConfig,
|
||||
RelayPaginationProp as RelayModernPaginationProp,
|
||||
RelayRefetchProp as RelayModernRefetchProp
|
||||
RelayRefetchProp as RelayModernRefetchProp,
|
||||
} from "./index";
|
||||
export { ConcreteFragment, ConcreteRequest, ConcreteBatchRequest } from "relay-runtime";
|
||||
import * as RelayRuntimeTypes from "relay-runtime";
|
||||
import { RelayEnvironmentInterface } from "./classic";
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
// Maybe Fix
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
export type ConcreteFragment = any;
|
||||
export type ConcreteBatch = any;
|
||||
export type ConcreteFragmentDefinition = object;
|
||||
export type ConcreteOperationDefinition = object;
|
||||
|
||||
@@ -30,7 +25,6 @@ export interface StatelessWithFragment<T> extends React.StatelessComponent<T> {
|
||||
getFragment: typeof getFragment;
|
||||
}
|
||||
export type ReactFragmentComponent<T> = ComponentWithFragment<T> | StatelessWithFragment<T>;
|
||||
export type ReactBaseComponent<T> = React.ComponentClass<T> | React.StatelessComponent<T>;
|
||||
export type RelayClassicEnvironment = RelayEnvironmentInterface;
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -68,18 +62,18 @@ export interface GeneratedNodeMap {
|
||||
}
|
||||
|
||||
export function createFragmentContainer<T>(
|
||||
Component: ReactBaseComponent<T>,
|
||||
Component: React.ComponentType<T>,
|
||||
fragmentSpec: RelayRuntimeTypes.GraphQLTaggedNode | GeneratedNodeMap
|
||||
): ReactFragmentComponent<T>;
|
||||
|
||||
export function createRefetchContainer<T>(
|
||||
Component: ReactBaseComponent<T>,
|
||||
Component: React.ComponentType<T>,
|
||||
fragmentSpec: RelayRuntimeTypes.GraphQLTaggedNode | GeneratedNodeMap,
|
||||
taggedNode: RelayRuntimeTypes.GraphQLTaggedNode
|
||||
): ReactFragmentComponent<T>;
|
||||
|
||||
export function createPaginationContainer<T>(
|
||||
Component: ReactBaseComponent<T>,
|
||||
Component: React.ComponentType<T>,
|
||||
fragmentSpec: RelayRuntimeTypes.GraphQLTaggedNode | GeneratedNodeMap,
|
||||
connectionConfig: ConnectionConfig<T>
|
||||
): ReactFragmentComponent<T>;
|
||||
|
||||
63
types/react-relay/index.d.ts
vendored
63
types/react-relay/index.d.ts
vendored
@@ -11,20 +11,36 @@ export {
|
||||
commitLocalUpdate,
|
||||
commitRelayModernMutation as commitMutation,
|
||||
fetchRelayModernQuery as fetchQuery,
|
||||
GraphQLTaggedNode,
|
||||
requestRelaySubscription as requestSubscription,
|
||||
} from "relay-runtime";
|
||||
|
||||
import * as React from "react";
|
||||
import * as RelayRuntimeTypes from "relay-runtime";
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
// Utility types
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
// Taken from https://github.com/pelotom/type-zoo
|
||||
// tslint:disable-next-line:strict-export-declare-modifiers
|
||||
type Diff<T extends string, U extends string> = ({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T];
|
||||
// tslint:disable-next-line:strict-export-declare-modifiers
|
||||
type Omit<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>;
|
||||
|
||||
export type RemoveRelayProp<P> = Omit<P & { relay: never }, "relay">;
|
||||
|
||||
export interface ComponentRef {
|
||||
componentRef?: (ref: any) => void;
|
||||
}
|
||||
|
||||
export type RelayContainer<T> = React.ComponentType<RemoveRelayProp<T> & ComponentRef>;
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
// Maybe Fix
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
export type ConcreteFragment = any;
|
||||
export type ConcreteBatch = any;
|
||||
export type ConcreteFragmentDefinition = object;
|
||||
export type ConcreteOperationDefinition = object;
|
||||
export type ReactBaseComponent<T> = React.ComponentClass<T> | React.StatelessComponent<T>;
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
// RelayProp
|
||||
@@ -40,24 +56,19 @@ export interface RelayProp {
|
||||
export function RelayQL(strings: string[], ...substitutions: any[]): RelayRuntimeTypes.RelayConcreteNode;
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
// RelayModernGraphQLTag
|
||||
// ReactRelayTypes
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
export interface GeneratedNodeMap {
|
||||
[key: string]: GraphQLTaggedNode;
|
||||
[key: string]: RelayRuntimeTypes.GraphQLTaggedNode;
|
||||
}
|
||||
export type GraphQLTaggedNode =
|
||||
| (() => ConcreteFragment | ConcreteBatch)
|
||||
| {
|
||||
modern(): ConcreteFragment | ConcreteBatch;
|
||||
classic(relayQL: typeof RelayQL): ConcreteFragmentDefinition | ConcreteOperationDefinition;
|
||||
};
|
||||
|
||||
/**
|
||||
* Runtime function to correspond to the `graphql` tagged template function.
|
||||
* All calls to this function should be transformed by the plugin.
|
||||
*/
|
||||
export interface GraphqlInterface {
|
||||
(strings: string[] | TemplateStringsArray): GraphQLTaggedNode;
|
||||
experimental(strings: string[] | TemplateStringsArray): GraphQLTaggedNode;
|
||||
(strings: string[] | TemplateStringsArray): RelayRuntimeTypes.GraphQLTaggedNode;
|
||||
experimental(strings: string[] | TemplateStringsArray): RelayRuntimeTypes.GraphQLTaggedNode;
|
||||
}
|
||||
export const graphql: GraphqlInterface;
|
||||
|
||||
@@ -67,7 +78,7 @@ export const graphql: GraphqlInterface;
|
||||
export interface QueryRendererProps {
|
||||
cacheConfig?: RelayRuntimeTypes.CacheConfig;
|
||||
environment: RelayRuntimeTypes.Environment;
|
||||
query: GraphQLTaggedNode;
|
||||
query: RelayRuntimeTypes.GraphQLTaggedNode;
|
||||
render(readyState: ReadyState): React.ReactElement<any> | undefined | null;
|
||||
variables: RelayRuntimeTypes.Variables;
|
||||
rerunParamExperimental?: RelayRuntimeTypes.RerunParam;
|
||||
@@ -87,9 +98,9 @@ export class QueryRenderer extends ReactRelayQueryRenderer {}
|
||||
// createFragmentContainer
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
export function createFragmentContainer<T>(
|
||||
Component: ReactBaseComponent<T>,
|
||||
fragmentSpec: GraphQLTaggedNode | GeneratedNodeMap
|
||||
): ReactBaseComponent<T>;
|
||||
Component: React.ComponentType<T>,
|
||||
fragmentSpec: RelayRuntimeTypes.GraphQLTaggedNode | GeneratedNodeMap
|
||||
): RelayContainer<T>;
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
// createPaginationContainer
|
||||
@@ -131,16 +142,16 @@ export interface ConnectionConfig<T> {
|
||||
paginationInfo: { count: number; cursor?: string },
|
||||
fragmentVariables: RelayRuntimeTypes.Variables
|
||||
): RelayRuntimeTypes.Variables;
|
||||
query: GraphQLTaggedNode;
|
||||
query: RelayRuntimeTypes.GraphQLTaggedNode;
|
||||
}
|
||||
export function createPaginationContainer<T>(
|
||||
Component: ReactBaseComponent<T>,
|
||||
fragmentSpec: GraphQLTaggedNode | GeneratedNodeMap,
|
||||
Component: React.ComponentType<T>,
|
||||
fragmentSpec: RelayRuntimeTypes.GraphQLTaggedNode | GeneratedNodeMap,
|
||||
connectionConfig: ConnectionConfig<T>
|
||||
): ReactBaseComponent<T>;
|
||||
): RelayContainer<T>;
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
// createFragmentContainer
|
||||
// createRefetchContainer
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
export interface RefetchOptions {
|
||||
force?: boolean;
|
||||
@@ -157,7 +168,7 @@ export type RelayRefetchProp = RelayProp & {
|
||||
): RelayRuntimeTypes.Disposable;
|
||||
};
|
||||
export function createRefetchContainer<T>(
|
||||
Component: ReactBaseComponent<T>,
|
||||
fragmentSpec: GraphQLTaggedNode | GeneratedNodeMap,
|
||||
taggedNode: GraphQLTaggedNode
|
||||
): ReactBaseComponent<T>;
|
||||
Component: React.ComponentType<T>,
|
||||
fragmentSpec: RelayRuntimeTypes.GraphQLTaggedNode | GeneratedNodeMap,
|
||||
taggedNode: RelayRuntimeTypes.GraphQLTaggedNode
|
||||
): RelayContainer<T>;
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import * as React from "react";
|
||||
import * as Relay from "react-relay/classic";
|
||||
|
||||
import { CompatContainer } from "./react-relay-compat-tests";
|
||||
|
||||
interface Props {
|
||||
text: string;
|
||||
userId: string;
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-empty-interface
|
||||
interface Response {}
|
||||
|
||||
export default class AddTweetMutation extends Relay.Mutation<Props, Response> {
|
||||
export default class AddTweetMutation extends Relay.Mutation<Props, {}> {
|
||||
getMutation() {
|
||||
return Relay.QL`mutation{addTweet}`;
|
||||
}
|
||||
@@ -64,6 +63,7 @@ const ArtworkContainer = Relay.createContainer(Artwork, {
|
||||
artwork: () => Relay.QL`
|
||||
fragment on Artwork {
|
||||
title
|
||||
${CompatContainer.getFragment("whatever")}
|
||||
}
|
||||
`,
|
||||
},
|
||||
@@ -83,7 +83,7 @@ class StubbedArtwork extends React.Component {
|
||||
setVariables: () => {},
|
||||
forceFetch: () => {},
|
||||
hasOptimisticUpdate: () => false,
|
||||
getPendingTransactions: (): Relay.RelayMutationTransaction[] => [],
|
||||
getPendingTransactions: (): any => undefined,
|
||||
commitUpdate: () => {},
|
||||
},
|
||||
};
|
||||
|
||||
53
types/react-relay/test/react-relay-compat-tests.tsx
Normal file
53
types/react-relay/test/react-relay-compat-tests.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import * as React from "react";
|
||||
import {
|
||||
QueryRenderer as CompatQueryRenderer,
|
||||
createFragmentContainer as createFragmentContainerCompat,
|
||||
commitMutation as commitMutationCompat,
|
||||
CompatEnvironment,
|
||||
RelayPaginationProp as RelayPaginationPropCompat,
|
||||
} from "react-relay/compat";
|
||||
|
||||
import { configs, mutation, optimisticResponse } from "./react-relay-tests";
|
||||
|
||||
// testting compat mutation with classic environment
|
||||
function markNotificationAsReadCompat(environment: CompatEnvironment, source: string, storyID: string) {
|
||||
const variables = {
|
||||
input: {
|
||||
source,
|
||||
storyID,
|
||||
},
|
||||
};
|
||||
|
||||
commitMutationCompat(environment, {
|
||||
configs,
|
||||
mutation,
|
||||
optimisticResponse,
|
||||
variables,
|
||||
onCompleted: (response, errors) => {
|
||||
console.log("Response received from server.");
|
||||
},
|
||||
onError: err => console.error(err),
|
||||
updater: (store, data) => {
|
||||
const field = store.get(storyID);
|
||||
if (field) {
|
||||
field.setValue(data.story, "story");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
interface CompatProps {
|
||||
relay: RelayPaginationPropCompat;
|
||||
}
|
||||
|
||||
class CompatComponent extends React.Component<CompatProps> {
|
||||
markNotificationAsRead(source: string, storyID: string) {
|
||||
markNotificationAsReadCompat(this.props.relay.environment, source, storyID);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (<div/>);
|
||||
}
|
||||
}
|
||||
|
||||
export const CompatContainer = createFragmentContainerCompat(CompatComponent, {});
|
||||
@@ -1,9 +1,6 @@
|
||||
import * as React from "react";
|
||||
import { Environment, Network, RecordSource, Store, ConnectionHandler } from "relay-runtime";
|
||||
import { Environment, Network, RecordSource, Store, ConnectionHandler, FragmentReference } from "relay-runtime";
|
||||
|
||||
////////////////////////////
|
||||
// RELAY MODERN TESTS
|
||||
///////////////////////////
|
||||
import {
|
||||
graphql,
|
||||
commitMutation,
|
||||
@@ -13,7 +10,8 @@ import {
|
||||
requestSubscription,
|
||||
QueryRenderer,
|
||||
RelayRefetchProp,
|
||||
RelayPaginationProp
|
||||
RelayPaginationProp,
|
||||
RelayProp
|
||||
} from "react-relay";
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -54,63 +52,137 @@ const MyQueryRenderer = (props: { name: string }) => (
|
||||
/>
|
||||
);
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
// Modern FragmentContainer
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
const MyFragmentContainer = createFragmentContainer(
|
||||
class TodoListView extends React.Component {
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
},
|
||||
{
|
||||
item: graphql`
|
||||
fragment TodoItem_item on Todo {
|
||||
text
|
||||
isComplete
|
||||
}
|
||||
`,
|
||||
}
|
||||
);
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
// Modern RefetchContainer
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
interface StoryInterface {
|
||||
id: string;
|
||||
}
|
||||
interface FeedStoriesProps {
|
||||
relay: RelayRefetchProp;
|
||||
feed: {
|
||||
stories: { edges: Array<{ node: StoryInterface }> };
|
||||
};
|
||||
}
|
||||
class Story extends React.Component<{ story: StoryInterface }> {}
|
||||
class FeedStories extends React.Component<FeedStoriesProps> {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{this.props.feed.stories.edges.map(edge => <Story story={edge.node} key={edge.node.id} />)}
|
||||
<button onClick={() => this._loadMore} title="Load More" />
|
||||
</div>
|
||||
);
|
||||
|
||||
type StoryLike = (storyID: string) => void;
|
||||
|
||||
// Artifact produced by relay-compiler-language-typescript
|
||||
// tslint:disable-next-line:no-const-enum
|
||||
const enum _Story_story$ref {}
|
||||
type Story_story$ref = _Story_story$ref & FragmentReference;
|
||||
// tslint:disable-next-line:interface-over-type-literal
|
||||
type Story_story = {
|
||||
readonly id: string;
|
||||
readonly text: string;
|
||||
readonly isPublished: boolean;
|
||||
readonly " $refType": Story_story$ref;
|
||||
};
|
||||
|
||||
const Story = (() => {
|
||||
interface Props {
|
||||
relay: RelayRefetchProp;
|
||||
story: Story_story;
|
||||
onLike: StoryLike;
|
||||
ignoreMe?: {};
|
||||
}
|
||||
|
||||
_loadMore() {
|
||||
// Increments the number of stories being rendered by 10.
|
||||
const refetchVariables = (fragmentVariables: { count: number }) => ({
|
||||
count: fragmentVariables.count + 10,
|
||||
interface State {
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
class Story extends React.Component<Props> {
|
||||
state = {
|
||||
isLoading: false
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
setInterval(this.handleRefresh.bind(this), 1000);
|
||||
}
|
||||
|
||||
handleRefresh() {
|
||||
this.setState({ isLoading: true });
|
||||
this.props.relay.refetch({ id: this.props.story.id }, {}, error => {
|
||||
this.setState({ isLoading: false });
|
||||
}, { force: true });
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{this.props.story.isPublished ? "" : "Draft: "}
|
||||
{this.props.story.text}
|
||||
{this.state.isLoading && <span>♺</span>}
|
||||
<button onClick={() => this.props.onLike(this.props.story.id)}>LIKE</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const StoryRefetchContainer = createRefetchContainer(
|
||||
Story,
|
||||
{
|
||||
story: graphql`
|
||||
fragment Story_story on Todo {
|
||||
id
|
||||
text
|
||||
isPublished
|
||||
}
|
||||
`,
|
||||
},
|
||||
graphql.experimental`
|
||||
query StoryRefetchQuery($id: ID!) {
|
||||
story(id: $id) {
|
||||
...Story_story
|
||||
}
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
function doesNotRequireRelayPropToBeProvided() {
|
||||
const onLike = (id: string) => console.log(`Liked story #${id}`);
|
||||
const story: { " $fragmentRefs": Story_story$ref } = {} as any;
|
||||
// TODO: Fix requirement to cast fragment reference as `any`.
|
||||
<StoryRefetchContainer story={story as any} onLike={onLike} />;
|
||||
}
|
||||
|
||||
return StoryRefetchContainer;
|
||||
})();
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
// Modern FragmentContainer
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
// Artifact produced by relay-compiler-language-typescript
|
||||
// tslint:disable-next-line:no-const-enum
|
||||
const enum _FeedStories_feed$ref {}
|
||||
type FeedStories_feed$ref = _FeedStories_feed$ref & FragmentReference;
|
||||
// tslint:disable-next-line:interface-over-type-literal
|
||||
type FeedStories_feed = {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly id: string;
|
||||
readonly " $fragmentRefs": Story_story$ref;
|
||||
};
|
||||
}>;
|
||||
readonly " $refType": FeedStories_feed$ref;
|
||||
};
|
||||
|
||||
const Feed = (() => {
|
||||
interface Props {
|
||||
relay: RelayProp;
|
||||
feed: FeedStories_feed;
|
||||
onStoryLike: StoryLike;
|
||||
ignoreMe?: {};
|
||||
}
|
||||
|
||||
const FeedStories: React.SFC<Props> = ({ feed, onStoryLike, relay }) => {
|
||||
// TODO: Getting env here for no good reason other than needing to test it works.
|
||||
// If you have a good relavant example, please update!
|
||||
relay.environment;
|
||||
const stories = feed.edges.map(edge => {
|
||||
// TODO: Fix requirement to cast fragment reference as `any`.
|
||||
return <Story story={edge.node as any} key={edge.node.id} onLike={onStoryLike} />;
|
||||
});
|
||||
this.props.relay.refetch(refetchVariables);
|
||||
}
|
||||
}
|
||||
return <div>{stories}</div>;
|
||||
};
|
||||
|
||||
const FeedRefetchContainer = createRefetchContainer(
|
||||
FeedStories,
|
||||
{
|
||||
feed: graphql.experimental`
|
||||
fragment FeedStories_feed on Feed @argumentDefinitions(count: { type: "Int", defaultValue: 10 }) {
|
||||
stories(first: $count) {
|
||||
const FeedFragmentContainer = createFragmentContainer(
|
||||
FeedStories,
|
||||
{
|
||||
feed: graphql`
|
||||
fragment FeedStories_feed on Feed {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
@@ -118,104 +190,137 @@ const FeedRefetchContainer = createRefetchContainer(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
graphql.experimental`
|
||||
query FeedStoriesRefetchQuery($count: Int) {
|
||||
feed {
|
||||
...FeedStories_feed @arguments(count: $count)
|
||||
}
|
||||
`,
|
||||
}
|
||||
`
|
||||
);
|
||||
);
|
||||
|
||||
function doesNotRequireRelayPropToBeProvided() {
|
||||
const onStoryLike = (id: string) => console.log(`Liked story #${id}`);
|
||||
const feed: { " $fragmentRefs": FeedStories_feed$ref } = {} as any;
|
||||
// TODO: Fix requirement to cast fragment reference as `any`.
|
||||
<FeedFragmentContainer feed={feed as any} onStoryLike={onStoryLike} />;
|
||||
}
|
||||
|
||||
return FeedFragmentContainer;
|
||||
})();
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
// Modern PaginationContainer
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
interface FeedProps {
|
||||
user: { feed: { edges: Array<{ node: StoryInterface }> } };
|
||||
relay: RelayPaginationProp;
|
||||
}
|
||||
class Feed extends React.Component<FeedProps> {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{this.props.user.feed.edges.map(edge => <Story story={edge.node} key={edge.node.id} />)}
|
||||
<button onClick={() => this._loadMore()} title="Load More" />
|
||||
</div>
|
||||
);
|
||||
|
||||
// Artifact produced by relay-compiler-language-typescript
|
||||
// tslint:disable-next-line:no-const-enum
|
||||
const enum _UserFeed_user$ref {}
|
||||
type UserFeed_user$ref = _UserFeed_user$ref & FragmentReference;
|
||||
// tslint:disable-next-line:interface-over-type-literal
|
||||
type UserFeed_user = {
|
||||
readonly feed: {
|
||||
readonly pageInfo: {
|
||||
readonly endCursor?: string | null;
|
||||
readonly hasNextPage: boolean;
|
||||
};
|
||||
readonly " $fragmentRefs": FeedStories_feed$ref;
|
||||
};
|
||||
readonly " $refType": UserFeed_user$ref;
|
||||
};
|
||||
|
||||
() => {
|
||||
interface Props {
|
||||
relay: RelayPaginationProp;
|
||||
loadMoreTitle: string;
|
||||
user: UserFeed_user;
|
||||
ignoreMe?: {};
|
||||
}
|
||||
|
||||
_loadMore() {
|
||||
if (!this.props.relay.hasMore() || this.props.relay.isLoading()) {
|
||||
return;
|
||||
class UserFeed extends React.Component<Props> {
|
||||
render() {
|
||||
const onStoryLike = (id: string) => console.log(`Liked story #${id}`);
|
||||
// TODO: Fix requirement to cast fragment reference as `any`.
|
||||
const feed = this.props.user.feed as any;
|
||||
return (
|
||||
<div>
|
||||
<Feed feed={feed} onStoryLike={onStoryLike} />
|
||||
<button onClick={() => this._loadMore()} title={this.props.loadMoreTitle} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
this.props.relay.loadMore(
|
||||
10, // Fetch the next 10 feed items
|
||||
e => {
|
||||
console.log(e);
|
||||
_loadMore() {
|
||||
if (!this.props.relay.hasMore() || this.props.relay.isLoading()) {
|
||||
return;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const FeedPaginationContainer = createPaginationContainer(
|
||||
Feed,
|
||||
{
|
||||
user: graphql`
|
||||
fragment Feed_user on User {
|
||||
feed(
|
||||
first: $count
|
||||
after: $cursor
|
||||
orderby: $orderBy # other variables
|
||||
) @connection(key: "Feed_feed") {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
...Story_story
|
||||
this.props.relay.loadMore(
|
||||
10, // Fetch the next 10 feed items
|
||||
e => {
|
||||
console.log(e);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const UserFeedPaginationContainer = createPaginationContainer(
|
||||
UserFeed,
|
||||
{
|
||||
user: graphql`
|
||||
fragment UserFeed_user on User {
|
||||
feed(
|
||||
first: $count
|
||||
after: $cursor
|
||||
orderby: $orderBy # other variables
|
||||
) @connection(key: "Feed_feed") {
|
||||
...FeedStories_feed
|
||||
pageInfo {
|
||||
endCursor
|
||||
hasNextPage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
direction: "forward",
|
||||
getConnectionFromProps(props) {
|
||||
return props.user && props.user.feed;
|
||||
`,
|
||||
},
|
||||
getFragmentVariables(prevVars, totalCount) {
|
||||
return {
|
||||
...prevVars,
|
||||
count: totalCount,
|
||||
};
|
||||
},
|
||||
getVariables(props, { count, cursor }, fragmentVariables) {
|
||||
return {
|
||||
count,
|
||||
cursor,
|
||||
// in most cases, for variables other than connection filters like
|
||||
// `first`, `after`, etc. you may want to use the previous values.
|
||||
orderBy: fragmentVariables.orderBy,
|
||||
};
|
||||
},
|
||||
query: graphql`
|
||||
query FeedPaginationQuery($count: Int!, $cursor: String, $orderby: String!) {
|
||||
user {
|
||||
# You could reference the fragment defined previously.
|
||||
...Feed_user
|
||||
{
|
||||
direction: "forward",
|
||||
getConnectionFromProps(props) {
|
||||
// TODO: Fix requirement to have `edges` and both `pageInfo` details for forward and backward pagination
|
||||
return props.user && props.user.feed as any;
|
||||
},
|
||||
getFragmentVariables(prevVars, totalCount) {
|
||||
return {
|
||||
...prevVars,
|
||||
count: totalCount,
|
||||
};
|
||||
},
|
||||
getVariables(props, { count, cursor }, fragmentVariables) {
|
||||
return {
|
||||
count,
|
||||
cursor,
|
||||
// in most cases, for variables other than connection filters like
|
||||
// `first`, `after`, etc. you may want to use the previous values.
|
||||
orderBy: fragmentVariables.orderBy,
|
||||
};
|
||||
},
|
||||
query: graphql`
|
||||
query FeedPaginationQuery($count: Int!, $cursor: String, $orderby: String!) {
|
||||
user {
|
||||
# You could reference the fragment defined previously.
|
||||
...Feed_user
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
`,
|
||||
}
|
||||
);
|
||||
|
||||
function doesNotRequireRelayPropToBeProvided() {
|
||||
const user: { " $fragmentRefs": UserFeed_user$ref } = {} as any;
|
||||
// TODO: Fix requirement to cast fragment reference as `any`.
|
||||
<UserFeedPaginationContainer loadMoreTitle="Load More" user={user as any} />;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
// Modern Mutations
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
const mutation = graphql`
|
||||
export const mutation = graphql`
|
||||
mutation MarkReadNotificationMutation($input: MarkReadNotificationData!) {
|
||||
markReadNotification(data: $input) {
|
||||
notification {
|
||||
@@ -225,7 +330,7 @@ const mutation = graphql`
|
||||
}
|
||||
`;
|
||||
|
||||
const optimisticResponse = {
|
||||
export const optimisticResponse = {
|
||||
markReadNotification: {
|
||||
notification: {
|
||||
seenState: "SEEN",
|
||||
@@ -233,7 +338,7 @@ const optimisticResponse = {
|
||||
},
|
||||
};
|
||||
|
||||
const configs = [
|
||||
export const configs = [
|
||||
{
|
||||
type: "NODE_DELETE" as "NODE_DELETE",
|
||||
deletedIDFieldName: "destroyedShipId",
|
||||
@@ -325,150 +430,3 @@ requestSubscription(
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
////////////////////////////
|
||||
// RELAY COMPAT TESTS
|
||||
///////////////////////////
|
||||
import {
|
||||
QueryRenderer as CompatQueryRenderer,
|
||||
createFragmentContainer as createFragmentContainerCompat,
|
||||
commitMutation as commitMutationCompat,
|
||||
CompatEnvironment,
|
||||
RelayPaginationProp as RelayPaginationPropCompat,
|
||||
} from "react-relay/compat";
|
||||
|
||||
// testting compat mutation with classic environment
|
||||
function markNotificationAsReadCompat(environment: CompatEnvironment, source: string, storyID: string) {
|
||||
const variables = {
|
||||
input: {
|
||||
source,
|
||||
storyID,
|
||||
},
|
||||
};
|
||||
|
||||
commitMutationCompat(environment, {
|
||||
configs,
|
||||
mutation,
|
||||
optimisticResponse,
|
||||
variables,
|
||||
onCompleted: (response, errors) => {
|
||||
console.log("Response received from server.");
|
||||
},
|
||||
onError: err => console.error(err),
|
||||
updater: (store, data) => {
|
||||
const field = store.get(storyID);
|
||||
if (field) {
|
||||
field.setValue(data.story, "story");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
interface CompatProps {
|
||||
relay: RelayPaginationPropCompat;
|
||||
}
|
||||
|
||||
export class CompatComponent extends React.Component<CompatProps> {
|
||||
markNotificationAsRead(source: string, storyID: string) {
|
||||
markNotificationAsReadCompat(this.props.relay.environment, source, storyID);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (<div/>);
|
||||
}
|
||||
}
|
||||
|
||||
const CompatContainer = createFragmentContainerCompat(CompatComponent, {});
|
||||
|
||||
////////////////////////////
|
||||
// RELAY-CLASSIC TESTS
|
||||
///////////////////////////
|
||||
import * as Relay from "react-relay/classic";
|
||||
|
||||
interface Props {
|
||||
text: string;
|
||||
userId: string;
|
||||
}
|
||||
|
||||
export default class AddTweetMutation extends Relay.Mutation<Props, {}> {
|
||||
getMutation() {
|
||||
return Relay.QL`mutation{addTweet}`;
|
||||
}
|
||||
|
||||
getFatQuery() {
|
||||
return Relay.QL`
|
||||
fragment on AddTweetPayload {
|
||||
tweetEdge
|
||||
user
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
getConfigs() {
|
||||
return [
|
||||
{
|
||||
type: "RANGE_ADD",
|
||||
parentName: "user",
|
||||
parentID: this.props.userId,
|
||||
connectionName: "tweets",
|
||||
edgeName: "tweetEdge",
|
||||
rangeBehaviors: {
|
||||
"": "append",
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
getVariables() {
|
||||
return this.props;
|
||||
}
|
||||
}
|
||||
|
||||
interface ArtwokRelayVariables {
|
||||
artworkID: string;
|
||||
}
|
||||
|
||||
interface ArtworkProps extends Relay.RelayProps<ArtwokRelayVariables> {
|
||||
artwork: {
|
||||
title: string;
|
||||
};
|
||||
}
|
||||
|
||||
class Artwork extends React.Component<ArtworkProps> {
|
||||
render() {
|
||||
return <a href={`/artworks/${this.props.relay.variables.artworkID}`}>{this.props.artwork.title}</a>;
|
||||
}
|
||||
}
|
||||
|
||||
const ArtworkContainer = Relay.createContainer(Artwork, {
|
||||
fragments: {
|
||||
artwork: () => Relay.QL`
|
||||
fragment on Artwork {
|
||||
title
|
||||
${ CompatContainer.getFragment('whatever') }
|
||||
}
|
||||
`,
|
||||
},
|
||||
});
|
||||
|
||||
class StubbedArtwork extends React.Component {
|
||||
render() {
|
||||
const props = {
|
||||
artwork: { title: "CHAMPAGNE FORMICA FLAG" },
|
||||
relay: {
|
||||
route: {
|
||||
name: "champagne",
|
||||
},
|
||||
variables: {
|
||||
artworkID: "champagne-formica-flag",
|
||||
},
|
||||
setVariables: () => {},
|
||||
forceFetch: () => {},
|
||||
hasOptimisticUpdate: () => false,
|
||||
getPendingTransactions: (): any => undefined,
|
||||
commitUpdate: () => {},
|
||||
},
|
||||
};
|
||||
return <ArtworkContainer {...props} />;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
"classic.d.ts",
|
||||
"compat.d.ts",
|
||||
"test/react-relay-tests.tsx",
|
||||
"test/react-relay-compat-tests.tsx",
|
||||
"test/react-relay-classic-tests.tsx"
|
||||
]
|
||||
}
|
||||
27
types/relay-runtime/index.d.ts
vendored
27
types/relay-runtime/index.d.ts
vendored
@@ -17,8 +17,6 @@ export type RelayConcreteNode = any;
|
||||
export type RelayMutationTransaction = any;
|
||||
export type RelayMutationRequest = any;
|
||||
export type RelayQueryRequest = any;
|
||||
export type ConcreteFragment = any;
|
||||
export type ConcreteBatch = any;
|
||||
export type ConcreteFragmentDefinition = object;
|
||||
export type ConcreteOperationDefinition = object;
|
||||
|
||||
@@ -32,6 +30,23 @@ export type ConcreteOperationDefinition = object;
|
||||
*/
|
||||
export type RelayContainer = any;
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
// Used in artifacts
|
||||
// emitted by
|
||||
// relay-compiler
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
// File: https://github.com/facebook/relay/blob/fe0e70f10bbcba1fff89911313ea69f24569464b/packages/relay-runtime/util/RelayConcreteNode.js
|
||||
export type ConcreteFragment = any;
|
||||
export type ConcreteRequest = any;
|
||||
export type ConcreteBatchRequest = any;
|
||||
|
||||
export type RequestNode = ConcreteRequest | ConcreteBatchRequest;
|
||||
|
||||
// Using `enum` here to create a distinct type and `const` to ensure it doesn’t leave any generated code.
|
||||
// tslint:disable-next-line:no-const-enum
|
||||
export const enum FragmentReference {}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
// RelayQL
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -44,9 +59,9 @@ export interface GeneratedNodeMap {
|
||||
[key: string]: GraphQLTaggedNode;
|
||||
}
|
||||
export type GraphQLTaggedNode =
|
||||
| (() => ConcreteFragment | ConcreteBatch)
|
||||
| (() => ConcreteFragment | RequestNode)
|
||||
| {
|
||||
modern(): ConcreteFragment | ConcreteBatch;
|
||||
modern(): ConcreteFragment | RequestNode;
|
||||
classic(relayQL: RelayQL): ConcreteFragmentDefinition | ConcreteOperationDefinition;
|
||||
};
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -85,7 +100,7 @@ export interface PayloadError {
|
||||
* May return an Observable or Promise of a raw server response.
|
||||
*/
|
||||
export function FetchFunction(
|
||||
operation: ConcreteBatch,
|
||||
operation: RequestNode,
|
||||
variables: Variables,
|
||||
cacheConfig: CacheConfig,
|
||||
uploadables?: UploadableMap
|
||||
@@ -99,7 +114,7 @@ export function FetchFunction(
|
||||
* fourth parameter.
|
||||
*/
|
||||
export type SubscribeFunction = (
|
||||
operation: ConcreteBatch,
|
||||
operation: RequestNode,
|
||||
variables: Variables,
|
||||
cacheConfig: CacheConfig,
|
||||
observer: LegacyObserver<QueryPayload>
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { Environment, Network, RecordSource, Store, ConnectionHandler, ViewerHandler, RecordSourceInspector } from "relay-runtime";
|
||||
import {
|
||||
Environment,
|
||||
Network,
|
||||
RecordSource,
|
||||
Store,
|
||||
ConnectionHandler,
|
||||
ViewerHandler,
|
||||
RecordSourceInspector,
|
||||
} from "relay-runtime";
|
||||
|
||||
const source = new RecordSource();
|
||||
const store = new Store(source);
|
||||
|
||||
Reference in New Issue
Block a user