[Relay] Change imports to work as per normal usage.

This commit is contained in:
Eloy Durán
2017-10-11 15:17:25 +02:00
parent 9d1970bce0
commit 694fb9b228
11 changed files with 497 additions and 686 deletions
@@ -1,6 +1,6 @@
import { RelayRuntimeTypes, RelayCommonTypes } from 'relay-runtime';
import { RelayEnvironmentInterface } from './react-relay-classic';
import {} from './react-relay-modern';
import { RelayEnvironmentInterface } from 'react-relay/classic';
import {} from 'react-relay/modern';
// ~~~~~~~~~~~~~~~~~~~~~
// Maybe Fix
+19 -156
View File
@@ -1,160 +1,23 @@
// Type definitions for react-relay 0.9.2
// Type definitions for react-relay 1.3
// Project: https://github.com/facebook/relay
// Definitions by: Johannes Schickling <https://github.com/graphcool>
// Definitions by: Johannes Schickling <https://github.com/graphcool>, Matt Martin <https://github.com/voxmatt>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.3
// TypeScript Version: 2.4
declare module "react-relay" {
import * as React from "react";
import { RelayRuntimeTypes } from 'relay-runtime';
import * as ReactRelayModernTypes from 'react-relay/modern';
type ClientMutationID = string;
/** Fragments are a hash of functions */
interface Fragments {
[query: string]: ((variables?: RelayVariables) => string)
}
interface CreateContainerOpts {
initialVariables?: any
fragments: Fragments
prepareVariables?(prevVariables: RelayVariables): RelayVariables
}
interface RelayVariables {
[name: string]: any
}
/** add static getFragment method to the component constructor */
interface RelayContainerClass<T> extends React.ComponentClass<T> {
getFragment: ((q: string, v?: RelayVariables) => string)
}
interface RelayQueryRequestResolve {
response: any
}
type RelayMutationStatus =
'UNCOMMITTED' | // Transaction hasn't yet been sent to the server. Transaction can be committed or rolled back.
'COMMIT_QUEUED' | // Transaction was committed but another transaction with the same collision key is pending, so the transaction has been queued to send to the server.
'COLLISION_COMMIT_FAILED' | //Transaction was queued for commit but another transaction with the same collision key failed. All transactions in the collision queue, including this one, have been failed. Transaction can be recommitted or rolled back.
'COMMITTING' | // Transaction is waiting for the server to respond.
'COMMIT_FAILED';
class RelayMutationTransaction {
applyOptimistic(): RelayMutationTransaction;
commit(): RelayMutationTransaction | null;
recommit(): void;
rollback(): void;
getError(): Error;
getStatus(): RelayMutationStatus;
getHash(): string;
getID(): ClientMutationID;
}
interface RelayMutationRequest {
getQueryString(): string
getVariables(): RelayVariables
resolve(result: RelayQueryRequestResolve): any
reject(errors: any): any
}
interface RelayQueryRequest {
resolve(result: RelayQueryRequestResolve): any
reject(errors: any): any
getQueryString(): string
getVariables(): RelayVariables
getID(): string
getDebugName(): string
}
interface RelayNetworkLayer {
supports(...options: string[]): boolean
}
class DefaultNetworkLayer implements RelayNetworkLayer {
constructor(host: string, options?: any)
supports(...options: string[]): boolean
}
function createContainer<T>(component: React.ComponentClass<T> | React.StatelessComponent<T>, params?: CreateContainerOpts): RelayContainerClass<T>
function injectNetworkLayer(networkLayer: RelayNetworkLayer): any
function isContainer(component: React.ComponentClass<any>): boolean
function QL(...args: any[]): string
class Route {
constructor(params?: RelayVariables)
}
/**
* Relay Mutation class, where T are the props it takes and S is the returned payload from Relay.Store.update.
* S is typically dynamic as it depends on the data the app is currently using, but it's possible to always
* return some data in the payload using REQUIRED_CHILDREN which is where specifying S is the most useful.
*/
class Mutation<T,S> {
props: T
constructor(props: T)
static getFragment(q: string): string
}
interface Transaction {
getError(): Error
Status(): number
}
interface StoreUpdateCallbacks<T> {
onFailure?(transaction: Transaction): any
onSuccess?(response: T): any
}
interface Store {
commitUpdate(mutation: Mutation<any,any>, callbacks?: StoreUpdateCallbacks<any>): any
}
var Store: Store
class RootContainer extends React.Component<RootContainerProps,any> {}
interface RootContainerProps extends React.Props<RootContainer>{
Component: RelayContainerClass<any>
route: Route
renderLoading?(): JSX.Element
renderFetched?(data: any): JSX.Element
renderFailure?(error: Error, retry: Function): JSX.Element
}
type ReadyStateEvent =
'ABORT' |
'CACHE_RESTORED_REQUIRED' |
'CACHE_RESTORE_FAILED' |
'CACHE_RESTORE_START' |
'NETWORK_QUERY_ERROR' |
'NETWORK_QUERY_RECEIVED_ALL' |
'NETWORK_QUERY_RECEIVED_REQUIRED' |
'NETWORK_QUERY_START' |
'STORE_FOUND_ALL' |
'STORE_FOUND_REQUIRED';
interface OnReadyStateChange {
(readyState: {
ready: boolean,
done: boolean,
stale: boolean,
error?: Error,
events: Array<ReadyStateEvent>,
aborted: boolean
}): void
}
interface RelayProp {
readonly route: { name: string; }; // incomplete, also has params and queries
readonly variables: any;
readonly pendingVariables?: any | null;
setVariables(variables: any, onReadyStateChange?: OnReadyStateChange): void;
forceFetch(variables: any, onReadyStateChange?: OnReadyStateChange): void;
hasOptimisticUpdate(record: any): boolean;
getPendingTransactions(record: any): RelayMutationTransaction[];
commitUpdate: (mutation: Mutation<any,any>, callbacks?: StoreUpdateCallbacks<any>) => any;
}
}
// ~~~~~~~~~~~~~~~~~~~~~
// React-Relay Modern
// ~~~~~~~~~~~~~~~~~~~~~
export import QueryRenderer = ReactRelayModernTypes.ReactRelayQueryRenderer;
export import createFragmentContainer = ReactRelayModernTypes.createFragmentContainer;
export import createPaginationContainer = ReactRelayModernTypes.createPaginationContainer;
export import createRefetchContainer = ReactRelayModernTypes.createRefetchContainer;
export import graphql = ReactRelayModernTypes.graphql;
export import commitLocalUpdate = RelayRuntimeTypes.commitLocalUpdate;
export import commitMutation = RelayRuntimeTypes.commitRelayModernMutation;
export import fetchQuery = RelayRuntimeTypes.fetchRelayModernQuery;
export import requestSubscription = RelayRuntimeTypes.requestRelaySubscription;
// exported for convenience
export import ModernTypes = ReactRelayModernTypes;
@@ -0,0 +1,92 @@
import * as React from "react"
import * as Relay from "react-relay/classic"
interface Props {
text: string
userId: string
}
interface Response {
}
export default class AddTweetMutation extends Relay.Mutation<Props, Response> {
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 ArtworkProps {
artwork: {
title: string
},
relay: Relay.RelayProp,
}
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
}
`
}
})
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: (): Relay.RelayMutationTransaction[] => undefined,
commitUpdate: () => {},
}
}
return <ArtworkContainer {...props} />
}
}
+377 -24
View File
@@ -1,30 +1,383 @@
import * as React from "react"
import * as Relay from "react-relay"
import * as React from "react";
import {
Environment,
Network,
RecordSource,
Store,
ConnectionHandler,
} from 'relay-runtime';
interface Props {
text: string
userId: string
////////////////////////////
// RELAY MODERN TESTS
///////////////////////////
import {
graphql,
commitMutation,
createFragmentContainer,
createPaginationContainer,
createRefetchContainer,
requestSubscription,
QueryRenderer,
ModernTypes,
} from "react-relay";
// ~~~~~~~~~~~~~~~~~~~~~
// Modern Environment
// ~~~~~~~~~~~~~~~~~~~~~
function fetchQuery(operation: any, variables: any, cacheConfig: {}) {
return fetch('/graphql');
}
const network = Network.create(fetchQuery);
const source = new RecordSource();
const store = new Store(source);
const modernEnvironment = new Environment({ network, store });
interface Response {
// ~~~~~~~~~~~~~~~~~~~~~
// Modern QueryRenderer
// ~~~~~~~~~~~~~~~~~~~~~
const MyQueryRenderer = (props: { name: string}) => (
<QueryRenderer
environment={ modernEnvironment }
query={graphql`
query ExampleQuery($pageID: ID!) {
page(id: $pageID) {
name
}
}
`}
variables={{
pageID: '110798995619330',
}}
render={({error, props}) => {
if (error) {
return <div>{error.message}</div>;
} else if (props) {
return <div>{props.name} is great!</div>;
}
return <div>Loading</div>;
}}
/>
);
// ~~~~~~~~~~~~~~~~~~~~~
// 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: ModernTypes.RelayRefetchProp;
feed: {
stories: { edges: Array<{ node: StoryInterface }> }
};
}
export default class AddTweetMutation extends Relay.Mutation<Props, Response> {
getMutation () {
return Relay.QL`mutation{addTweet}`
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>
);
}
getFatQuery () {
_loadMore() {
// Increments the number of stories being rendered by 10.
const refetchVariables = (fragmentVariables: {count: number }) => ({
count: fragmentVariables.count + 10,
});
this.props.relay.refetch(refetchVariables);
}
}
const FeedRefetchContainer = createRefetchContainer(
FeedStories,
{
feed: graphql.experimental`
fragment FeedStories_feed on Feed
@argumentDefinitions(
count: {type: "Int", defaultValue: 10}
) {
stories(first: $count) {
edges {
node {
id
...Story_story
}
}
}
}
`
},
graphql.experimental`
query FeedStoriesRefetchQuery($count: Int) {
feed {
...FeedStories_feed @arguments(count: $count)
}
}
`,
);
// ~~~~~~~~~~~~~~~~~~~~~
// Modern PaginationContainer
// ~~~~~~~~~~~~~~~~~~~~~
interface FeedProps {
user: { feed: { edges: Array<{ node: StoryInterface}>}};
relay: ModernTypes.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>);
}
_loadMore() {
if (!this.props.relay.hasMore() || this.props.relay.isLoading()) {
return;
}
this.props.relay.loadMore(
10, // Fetch the next 10 feed items
e => {
console.log(e);
},
);
}
}
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
}
}
}
}
`,
},
{
direction: 'forward',
getConnectionFromProps(props: { user: {feed: any}}) {
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
}
}
`
}
);
// ~~~~~~~~~~~~~~~~~~~~~
// Modern Mutations
// ~~~~~~~~~~~~~~~~~~~~~
const mutation = graphql`
mutation MarkReadNotificationMutation(
$input: MarkReadNotificationData!
) {
markReadNotification(data: $input) {
notification {
seenState
}
}
}
`;
const optimisticResponse = {
markReadNotification: {
notification: {
seenState: 'SEEN',
},
},
};
const configs = [{
type: 'NODE_DELETE' as 'NODE_DELETE',
deletedIDFieldName: 'destroyedShipId',
}, {
type: 'RANGE_ADD' as 'RANGE_ADD',
parentID: 'shipId',
connectionInfo: [{
key: 'AddShip_ships',
rangeBehavior: 'append',
}],
edgeName: 'newShipEdge',
}, {
type: 'RANGE_DELETE' as 'RANGE_DELETE',
parentID: 'todoId',
connectionKeys: [{
key: 'RemoveTags_tags',
rangeBehavior: 'append',
}],
pathToConnection: ['todo', 'tags'],
deletedIDFieldName: 'removedTagId'
}];
function markNotificationAsRead(source: string, storyID: string) {
const variables = {
input: {
source,
storyID,
},
};
commitMutation(
modernEnvironment,
{
configs,
mutation,
optimisticResponse,
variables,
onCompleted: (response, errors) => {
console.log('Response received from server.');
},
onError: err => console.error(err),
},
);
}
// ~~~~~~~~~~~~~~~~~~~~~
// Modern Subscriptions
// ~~~~~~~~~~~~~~~~~~~~~
const subscription = graphql`
subscription MarkReadNotificationSubscription(
$storyID: ID!
) {
markReadNotification(storyID: $storyID) {
notification {
seenState
}
}
}
`;
const variables = {
storyID: '123',
};
requestSubscription(
modernEnvironment, // see Environment docs
{
subscription,
variables,
// optional but recommended:
onCompleted: () => {},
onError: error => console.error(error),
// example of a custom updater
updater: store => {
// Get the notification
const rootField = store.getRootField('markReadNotification');
const notification = !!rootField && rootField.getLinkedRecord('notification');
// Add it to a connection
const viewer = store.getRoot().getLinkedRecord('viewer');
const notifications =
ConnectionHandler.getConnection(viewer, 'notifications');
const edge = ConnectionHandler.createEdge(
store,
notifications,
notification,
'<TypeOfNotificationsEdge>',
);
ConnectionHandler.insertEdgeAfter(notifications, edge);
},
}
);
////////////////////////////
// RELAY COMPAT TESTS
///////////////////////////
import {
QueryRenderer as CompatQueryRenderer,
createFragmentContainer as createFragmentContainerCompat
} from "react-relay/compat";
// TODO? This is all more or less identical to modern...
////////////////////////////
// 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 () {
getConfigs() {
return [{
type: "RANGE_ADD",
parentName: "user",
@@ -34,19 +387,19 @@ export default class AddTweetMutation extends Relay.Mutation<Props, Response> {
rangeBehaviors: {
"": "append",
},
}]
}];
}
getVariables () {
return this.props
getVariables() {
return this.props;
}
}
interface ArtworkProps {
artwork: {
title: string
},
relay: Relay.RelayProp,
};
relay: Relay.RelayProp;
}
class Artwork extends React.Component<ArtworkProps> {
@@ -55,7 +408,7 @@ class Artwork extends React.Component<ArtworkProps> {
<a href={`/artworks/${this.props.relay.variables.artworkID}`}>
{this.props.artwork.title}
</a>
)
);
}
}
@@ -67,7 +420,7 @@ const ArtworkContainer = Relay.createContainer(Artwork, {
}
`
}
})
});
class StubbedArtwork extends React.Component {
render() {
@@ -83,10 +436,10 @@ class StubbedArtwork extends React.Component {
setVariables: () => {},
forceFetch: () => {},
hasOptimisticUpdate: () => false,
getPendingTransactions: (): Relay.RelayMutationTransaction[] => undefined,
getPendingTransactions: (): any => undefined,
commitUpdate: () => {},
}
}
return <ArtworkContainer {...props} />
};
return <ArtworkContainer {...props} />;
}
}
+7 -3
View File
@@ -7,7 +7,7 @@
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": false,
"strictNullChecks": true,
"baseUrl": "../",
"typeRoots": [
"../"
@@ -19,6 +19,10 @@
},
"files": [
"index.d.ts",
"react-relay-tests.tsx"
"react-relay-tests.tsx",
"react-relay-classic-tests.tsx",
"classic.d.ts",
"modern.d.ts",
"compat.d.ts"
]
}
}
-23
View File
@@ -1,23 +0,0 @@
// Type definitions for react-relay 1.3
// Project: https://github.com/facebook/relay
// Definitions by: Johannes Schickling <https://github.com/graphcool>, Matt Martin <https://github.com/voxmatt>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
import { RelayRuntimeTypes } from 'relay-runtime';
import * as ReactRelayModernTypes from './lib/react-relay-modern';
// ~~~~~~~~~~~~~~~~~~~~~
// React-Relay Modern
// ~~~~~~~~~~~~~~~~~~~~~
export import QueryRenderer = ReactRelayModernTypes.ReactRelayQueryRenderer;
export import createFragmentContainer = ReactRelayModernTypes.createFragmentContainer;
export import createPaginationContainer = ReactRelayModernTypes.createPaginationContainer;
export import createRefetchContainer = ReactRelayModernTypes.createRefetchContainer;
export import graphql = ReactRelayModernTypes.graphql;
export import commitLocalUpdate = RelayRuntimeTypes.commitLocalUpdate;
export import commitMutation = RelayRuntimeTypes.commitRelayModernMutation;
export import fetchQuery = RelayRuntimeTypes.fetchRelayModernQuery;
export import requestSubscription = RelayRuntimeTypes.requestRelaySubscription;
// exported for convenience
export import ModernTypes = ReactRelayModernTypes;
-445
View File
@@ -1,445 +0,0 @@
import * as React from "react";
import {
Environment,
Network,
RecordSource,
Store,
ConnectionHandler,
} from 'relay-runtime';
////////////////////////////
// RELAY MODERN TESTS
///////////////////////////
import {
graphql,
commitMutation,
createFragmentContainer,
createPaginationContainer,
createRefetchContainer,
requestSubscription,
QueryRenderer,
ModernTypes,
} from "react-relay";
// ~~~~~~~~~~~~~~~~~~~~~
// Modern Environment
// ~~~~~~~~~~~~~~~~~~~~~
function fetchQuery(operation: any, variables: any, cacheConfig: {}) {
return fetch('/graphql');
}
const network = Network.create(fetchQuery);
const source = new RecordSource();
const store = new Store(source);
const modernEnvironment = new Environment({ network, store });
// ~~~~~~~~~~~~~~~~~~~~~
// Modern QueryRenderer
// ~~~~~~~~~~~~~~~~~~~~~
const MyQueryRenderer = (props: { name: string}) => (
<QueryRenderer
environment={ modernEnvironment }
query={graphql`
query ExampleQuery($pageID: ID!) {
page(id: $pageID) {
name
}
}
`}
variables={{
pageID: '110798995619330',
}}
render={({error, props}) => {
if (error) {
return <div>{error.message}</div>;
} else if (props) {
return <div>{props.name} is great!</div>;
}
return <div>Loading</div>;
}}
/>
);
// ~~~~~~~~~~~~~~~~~~~~~
// 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: ModernTypes.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>
);
}
_loadMore() {
// Increments the number of stories being rendered by 10.
const refetchVariables = (fragmentVariables: {count: number }) => ({
count: fragmentVariables.count + 10,
});
this.props.relay.refetch(refetchVariables);
}
}
const FeedRefetchContainer = createRefetchContainer(
FeedStories,
{
feed: graphql.experimental`
fragment FeedStories_feed on Feed
@argumentDefinitions(
count: {type: "Int", defaultValue: 10}
) {
stories(first: $count) {
edges {
node {
id
...Story_story
}
}
}
}
`
},
graphql.experimental`
query FeedStoriesRefetchQuery($count: Int) {
feed {
...FeedStories_feed @arguments(count: $count)
}
}
`,
);
// ~~~~~~~~~~~~~~~~~~~~~
// Modern PaginationContainer
// ~~~~~~~~~~~~~~~~~~~~~
interface FeedProps {
user: { feed: { edges: Array<{ node: StoryInterface}>}};
relay: ModernTypes.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>);
}
_loadMore() {
if (!this.props.relay.hasMore() || this.props.relay.isLoading()) {
return;
}
this.props.relay.loadMore(
10, // Fetch the next 10 feed items
e => {
console.log(e);
},
);
}
}
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
}
}
}
}
`,
},
{
direction: 'forward',
getConnectionFromProps(props: { user: {feed: any}}) {
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
}
}
`
}
);
// ~~~~~~~~~~~~~~~~~~~~~
// Modern Mutations
// ~~~~~~~~~~~~~~~~~~~~~
const mutation = graphql`
mutation MarkReadNotificationMutation(
$input: MarkReadNotificationData!
) {
markReadNotification(data: $input) {
notification {
seenState
}
}
}
`;
const optimisticResponse = {
markReadNotification: {
notification: {
seenState: 'SEEN',
},
},
};
const configs = [{
type: 'NODE_DELETE' as 'NODE_DELETE',
deletedIDFieldName: 'destroyedShipId',
}, {
type: 'RANGE_ADD' as 'RANGE_ADD',
parentID: 'shipId',
connectionInfo: [{
key: 'AddShip_ships',
rangeBehavior: 'append',
}],
edgeName: 'newShipEdge',
}, {
type: 'RANGE_DELETE' as 'RANGE_DELETE',
parentID: 'todoId',
connectionKeys: [{
key: 'RemoveTags_tags',
rangeBehavior: 'append',
}],
pathToConnection: ['todo', 'tags'],
deletedIDFieldName: 'removedTagId'
}];
function markNotificationAsRead(source: string, storyID: string) {
const variables = {
input: {
source,
storyID,
},
};
commitMutation(
modernEnvironment,
{
configs,
mutation,
optimisticResponse,
variables,
onCompleted: (response, errors) => {
console.log('Response received from server.');
},
onError: err => console.error(err),
},
);
}
// ~~~~~~~~~~~~~~~~~~~~~
// Modern Subscriptions
// ~~~~~~~~~~~~~~~~~~~~~
const subscription = graphql`
subscription MarkReadNotificationSubscription(
$storyID: ID!
) {
markReadNotification(storyID: $storyID) {
notification {
seenState
}
}
}
`;
const variables = {
storyID: '123',
};
requestSubscription(
modernEnvironment, // see Environment docs
{
subscription,
variables,
// optional but recommended:
onCompleted: () => {},
onError: error => console.error(error),
// example of a custom updater
updater: store => {
// Get the notification
const rootField = store.getRootField('markReadNotification');
const notification = !!rootField && rootField.getLinkedRecord('notification');
// Add it to a connection
const viewer = store.getRoot().getLinkedRecord('viewer');
const notifications =
ConnectionHandler.getConnection(viewer, 'notifications');
const edge = ConnectionHandler.createEdge(
store,
notifications,
notification,
'<TypeOfNotificationsEdge>',
);
ConnectionHandler.insertEdgeAfter(notifications, edge);
},
}
);
////////////////////////////
// RELAY COMPAT TESTS
///////////////////////////
import {
QueryRenderer as CompatQueryRenderer,
createFragmentContainer as createFragmentContainerCompat
} from "react-relay/compat";
// TODO? This is all more or less identical to modern...
////////////////////////////
// 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 ArtworkProps {
artwork: {
title: string
};
relay: Relay.RelayProp;
}
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
}
`
}
});
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} />;
}
}
-33
View File
@@ -1,33 +0,0 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6",
"dom"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"baseUrl": "../../",
"typeRoots": [
"../../"
],
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true,
"jsx": "react",
"paths": {
"react-relay": ["react-relay/v1"],
"react-relay/*": ["react-relay/v1/*"],
"react-relay/classic": ["react-relay/v1/classic.d"],
"react-relay/compat": ["react-relay/v1/compat.d"]
}
},
"files": [
"index.d.ts",
"react-relay-tests.tsx",
"lib/react-relay-classic.d.ts",
"lib/react-relay-modern.d.ts",
"lib/react-relay-compat.d.ts"
]
}