diff --git a/website/docs/community/learning-resources.md b/website/docs/community/learning-resources.md index a2dd71c5a3a05..52cc51dd8c630 100644 --- a/website/docs/community/learning-resources.md +++ b/website/docs/community/learning-resources.md @@ -8,26 +8,56 @@ import useBaseUrl from '@docusaurus/useBaseUrl'; import DocsRating from '@site/src/core/DocsRating'; import {FbInternalOnly, OssOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; +## Guides and articles: + +- [How to use @argumentsDefinitions to define local variables to your fragments](https://medium.com/entria/relay-modern-argumentdefinitions-d53769dbb95d) (by Entria) +- [Deep Dive of Updater Relay Store function. How to update your store properly after a mutation or subscription](https://medium.com/entria/wrangling-the-client-store-with-the-relay-modern-updater-function-5c32149a71ac) (by Entria) +- [Optimistic Update: how to update your UI before server responds](https://medium.com/entria/relay-modern-optimistic-update-a09ba22d83c9) (by Entria) +- [Relay Network Deep Dive - how to incrementally improve your network layer to manage complex data fetching requirements](https://medium.com/entria/relay-modern-network-deep-dive-ec187629dfd3) (by Entria) +- [Relay Modern with TypeScript - how to configure Relay Modern to make it with TypeScript](https://medium.com/@sibelius/relay-modern-migration-to-typescript-c26ab0ee749c) (by @sibelius) +- [Collection of random thoughts and discoveries around Relay](https://mrtnzlml.com/docs/relay) + + + ## Relay example projects -These projects serve as an example of how to use Relay in real world applications. +These projects serve as an example of how to use Relay in real world applications. Some of them are even with educational videos. - [github.com/relayjs/relay-examples](https://github.com/relayjs/relay-examples) +- [github.com/adeira/relay-example](https://github.com/adeira/relay-example) +- [github.com/juffalow/react-relay-example](https://github.com/juffalow/react-relay-example) -## Guides and articles: +## Learn basics +Here, you will find articles written by Relay community. They are touching basic topic which are necessary for your daily work. + +- [What is a fragment? Basic explanation of what is a fragment and what it is used for](https://medium.com/@sibelius/relay-modern-what-is-a-fragment-c70f164c2469) (by @sibelius) +- [Relay anti-patterns. What you should avoid doing when using Relay concepts](https://medium.com/entria/relay-apollo-anti-pattern-d9f4dea47738) (by Entria) +- [Insights of how Relay Modern has improved a lot since Relay Classic](https://medium.com/entria/relay-is-just-getting-better-54112ffc1a9e) (by Entria) - [How to use @argumentsDefinitions to define local variables to your fragments](https://medium.com/entria/relay-modern-argumentdefinitions-d53769dbb95d) (by Entria) +- [How to paginate using a Refetch Container. You can use a refetch container to paginate as well, just use renderVariables correctly](https://medium.com/entria/relay-modern-pagination-using-refetch-container-editing-a07c6b33ae4e) (by Entria) + +## About Relay Store + +- [How Relay Modern stores your data](https://medium.com/@sibelius/relay-modern-the-relay-store-8984cd148798) (by @sibelius) - [Deep Dive of Updater Relay Store function. How to update your store properly after a mutation or subscription](https://medium.com/entria/wrangling-the-client-store-with-the-relay-modern-updater-function-5c32149a71ac) (by Entria) - [Optimistic Update: how to update your UI before server responds](https://medium.com/entria/relay-modern-optimistic-update-a09ba22d83c9) (by Entria) +- [Local State Management, part 1 - how to create a controlled input using Relay](https://babangsund.com/relay_local_state_management/) (by @babangsund) +- [Local State Management, part 2 - how to manage global state and localStorage persistence on the client, using Relay](https://babangsund.com/relay_local_state_management_2/) (by @babangsund) +- [Local State Management, part 3 - using LocalQueryRenderer and local state to manage nested fragments](https://babangsund.com/relay_local_state_management_3/) (by @babangsund) + +## Network Layer + - [Relay Network Deep Dive - how to incrementally improve your network layer to manage complex data fetching requirements](https://medium.com/entria/relay-modern-network-deep-dive-ec187629dfd3) (by Entria) -- [Relay Modern with TypeScript - how to configure Relay Modern to make it with TypeScript](https://medium.com/@sibelius/relay-modern-migration-to-typescript-c26ab0ee749c) (by @sibelius) -- [Collection of random thoughts and discoveries around Relay](https://mrtnzlml.com/docs/relay) - +## Relay Configuration -## Relay Modern articles +- [Relay Modern with TypeScript - how to configure Relay Modern to make it with TypeScript](https://medium.com/@sibelius/relay-modern-migration-to-typescript-c26ab0ee749c) (by @sibelius) -Note: you can find many more resources by looking at the Relay Modern Documentation. +## Miscellaneous + +- [Relay Modern Learning Blog Posts Thread on Twitter](https://twitter.com/sseraphini/status/1078595758801203202) +- [Collection of random thoughts and discoveries around Relay](https://mrtnzlml.com/docs/relay) diff --git a/website/versioned_docs/version-v1.4.1/Community-LearningResources.md b/website/versioned_docs/version-v1.4.1/Community-LearningResources.md deleted file mode 100644 index 9dead61d5b35e..0000000000000 --- a/website/versioned_docs/version-v1.4.1/Community-LearningResources.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -id: community-learning-resources -title: Community Learning Resources ---- -## Relay example projects - -These projects serve as an example of how to use Relay in real world applications. Some of them are even with educational videos. - -- [github.com/relayjs/relay-examples](https://github.com/relayjs/relay-examples) -- [github.com/adeira/relay-example](https://github.com/adeira/relay-example) -- [github.com/juffalow/react-relay-example](https://github.com/juffalow/react-relay-example) - -## Learn basics - -Here, you will find articles written by Relay community. They are touching basic topic which are necessary for your daily work. - -- [What is a fragment? Basic explanation of what is a fragment and what it is used for](https://medium.com/@sibelius/relay-modern-what-is-a-fragment-c70f164c2469) (by @sibelius) -- [Relay anti-patterns. What you should avoid doing when using Relay concepts](https://medium.com/entria/relay-apollo-anti-pattern-d9f4dea47738) (by Entria) -- [Insights of how Relay Modern has improved a lot since Relay Classic](https://medium.com/entria/relay-is-just-getting-better-54112ffc1a9e) (by Entria) -- [How to use @argumentsDefinitions to define local variables to your fragments](https://medium.com/entria/relay-modern-argumentdefinitions-d53769dbb95d) (by Entria) -- [How to paginate using a Refetch Container. You can use a refetch container to paginate as well, just use renderVariables correctly](https://medium.com/entria/relay-modern-pagination-using-refetch-container-editing-a07c6b33ae4e) (by Entria) - -## About Relay Store - -- [How Relay Modern stores your data](https://medium.com/@sibelius/relay-modern-the-relay-store-8984cd148798) (by @sibelius) -- [Deep Dive of Updater Relay Store function. How to update your store properly after a mutation or subscription](https://medium.com/entria/wrangling-the-client-store-with-the-relay-modern-updater-function-5c32149a71ac) (by Entria) -- [Optimistic Update: how to update your UI before server responds](https://medium.com/entria/relay-modern-optimistic-update-a09ba22d83c9) (by Entria) -- [Local State Management, part 1 - how to create a controlled input using Relay](https://babangsund.com/relay_local_state_management/) (by @babangsund) -- [Local State Management, part 2 - how to manage global state and localStorage persistence on the client, using Relay](https://babangsund.com/relay_local_state_management_2/) (by @babangsund) -- [Local State Management, part 3 - using LocalQueryRenderer and local state to manage nested fragments](https://babangsund.com/relay_local_state_management_3/) (by @babangsund) - -## Network Layer - -- [Relay Network Deep Dive - how to incrementally improve your network layer to manage complex data fetching requirements](https://medium.com/entria/relay-modern-network-deep-dive-ec187629dfd3) (by Entria) - -## Relay Configuration - -- [Relay Modern with TypeScript - how to configure Relay Modern to make it with TypeScript](https://medium.com/@sibelius/relay-modern-migration-to-typescript-c26ab0ee749c) (by @sibelius) - -## Miscellaneous - -- [Relay Modern Learning Blog Posts Thread on Twitter](https://twitter.com/sseraphini/status/1078595758801203202) -- [Collection of random thoughts and discoveries around Relay](https://mrtnzlml.com/docs/relay) diff --git a/website/versioned_docs/version-v1.4.1/GraphQL-ServerSpecification.md b/website/versioned_docs/version-v1.4.1/GraphQL-ServerSpecification.md deleted file mode 100644 index 45e401445ba5b..0000000000000 --- a/website/versioned_docs/version-v1.4.1/GraphQL-ServerSpecification.md +++ /dev/null @@ -1,546 +0,0 @@ ---- -id: graphql-server-specification -title: GraphQL Server Specification -original_id: graphql-server-specification ---- - -import useBaseUrl from '@docusaurus/useBaseUrl'; - -The goal of this document is to specify the assumptions that Relay makes about a GraphQL server and demonstrate them through an example GraphQL schema. - -Table of Contents: - -- [Preface](#preface) -- [Schema](#schema) -- [Object Identification](#object-identification) -- [Connections](#connections) -- [Mutations](#mutations) -- [Further Reading](#further-reading) - -## Preface - -The three core assumptions that Relay makes about a GraphQL server are that it provides: - -1. A mechanism for refetching an object. -2. A description of how to page through connections. -3. Structure around mutations to make them predictable. - -This example demonstrates all three of these assumptions. This example is not comprehensive, but it is designed to quickly introduce these core assumptions, to provide some context before diving into the more detailed specification of the library. - -The premise of the example is that we want to use GraphQL to query for information about ships and factions in the original Star Wars trilogy. - -It is assumed that the reader is already familiar with [GraphQL](http://graphql.org/); if not, the README for [GraphQL.js](https://github.com/graphql/graphql-js) is a good place to start. - -It is also assumed that the reader is already familiar with [Star Wars](https://en.wikipedia.org/wiki/Star_Wars); if not, the 1977 version of Star Wars is a good place to start, though the 1997 Special Edition will serve for the purposes of this document. - -## Schema - -The schema described below will be used to demonstrate the functionality that a GraphQL server used by Relay should implement. The two core types are a faction and a ship in the Star Wars universe, where a faction has many ships associated with it. The schema below is the output of the GraphQL.js [`schemaPrinter`](https://github.com/graphql/graphql-js/blob/main/src/utilities/schemaPrinter.js). - -``` - -interface Node { - id: ID! -} - -type Faction : Node { - id: ID! - name: String - ships: ShipConnection -} - -type Ship : Node { - id: ID! - name: String -} - -type ShipConnection { - edges: [ShipEdge] - pageInfo: PageInfo! -} - -type ShipEdge { - cursor: String! - node: Ship -} - -type PageInfo { - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - endCursor: String -} - -type Query { - rebels: Faction - empire: Faction - node(id: ID!): Node -} - -input IntroduceShipInput { - factionId: String! - shipNamed: String! - clientMutationId: String! -} - -type IntroduceShipPayload { - faction: Faction - ship: Ship - clientMutationId: String! -} - -type Mutation { - introduceShip(input: IntroduceShipInput!): IntroduceShipPayload -} -``` - -## Object Identification - -Both `Faction` and `Ship` have identifiers that we can use to refetch them. We expose this capability to Relay through the `Node` interface and the `node` field on the root query type. - -The `Node` interface contains a single field, `id`, which is a `ID!`. The `node` root field takes a single argument, a `ID!`, and returns a `Node`. These two work in concert to allow refetching; if we pass the `id` returned in that field to the `node` field, we get the object back. - -Let's see this in action, and query for the ID of the rebels: - -``` - -query RebelsQuery { - rebels { - id - name - } -} -``` - -returns - -```json - -{ - "rebels": { - "id": "RmFjdGlvbjox", - "name": "Alliance to Restore the Republic" - } -} -``` - -So now we know the ID of the Rebels in our system. We can now refetch them: - -``` - -query RebelsRefetchQuery { - node(id: "RmFjdGlvbjox") { - id - ... on Faction { - name - } - } -} -``` - -returns - -```json - -{ - "node": { - "id": "RmFjdGlvbjox", - "name": "Alliance to Restore the Republic" - } -} -``` - -If we do the same thing with the Empire, we'll find that it returns a different ID, and we can refetch it as well: - -``` - -query EmpireQuery { - empire { - id - name - } -} -``` - -yields - -```json - -{ - "empire": { - "id": "RmFjdGlvbjoy", - "name": "Galactic Empire" - } -} -``` - -and - -``` - -query EmpireRefetchQuery { - node(id: "RmFjdGlvbjoy") { - id - ... on Faction { - name - } - } -} -``` - -yields - -```json - -{ - "node": { - "id": "RmFjdGlvbjoy", - "name": "Galactic Empire" - } -} -``` - -The `Node` interface and `node` field assume globally unique IDs for this refetching. A system without globally unique IDs can usually synthesize them by combining the type with the type-specific ID, which is what was done in this example. - -The IDs we got back were base64 strings. IDs are designed to be opaque (the only thing that should be passed to the `id` argument on `node` is the unaltered result of querying `id` on some object in the system), and base64ing a string is a useful convention in GraphQL to remind viewers that the string is an opaque identifier. - -Complete details on how the server should behave are available in the [GraphQL Object Identification](https://graphql.org/learn/global-object-identification/) spec. - -## Connections - -A faction has many ships in the Star Wars universe. Relay contains functionality to make manipulating one-to-many relationships easy, using a standardized way of expressing these one-to-many relationships. This standard connection model offers ways of slicing and paginating through the connection. - -Let's take the rebels, and ask for their first ship: - -``` - -query RebelsShipsQuery { - rebels { - name, - ships(first: 1) { - edges { - node { - name - } - } - } - } -} -``` - -yields - -```json - -{ - "rebels": { - "name": "Alliance to Restore the Republic", - "ships": { - "edges": [ - { - "node": { - "name": "X-Wing" - } - } - ] - } - } -} -``` - -That used the `first` argument to `ships` to slice the result set down to the first one. But what if we wanted to paginate through it? On each edge, a cursor will be exposed that we can use to paginate. Let's ask for the first two this time, and get the cursor as well: - -``` - -query MoreRebelShipsQuery { - rebels { - name, - ships(first: 2) { - edges { - cursor - node { - name - } - } - } - } -} -``` - -and we get back - -```json - -{ - "rebels": { - "name": "Alliance to Restore the Republic", - "ships": { - "edges": [ - { - "cursor": "YXJyYXljb25uZWN0aW9uOjA=", - "node": { - "name": "X-Wing" - } - }, - { - "cursor": "YXJyYXljb25uZWN0aW9uOjE=", - "node": { - "name": "Y-Wing" - } - } - ] - } - } -} -``` - -Notice that the cursor is a base64 string. That's the pattern from earlier: the server is reminding us that this is an opaque string. We can pass this string back to the server as the `after` argument to the `ships` field, which will let us ask for the next three ships after the last one in the previous result: - -``` - -query EndOfRebelShipsQuery { - rebels { - name, - ships(first: 3 after: "YXJyYXljb25uZWN0aW9uOjE=") { - edges { - cursor, - node { - name - } - } - } - } -} -``` - -gives us - -```json - - -{ - "rebels": { - "name": "Alliance to Restore the Republic", - "ships": { - "edges": [ - { - "cursor": "YXJyYXljb25uZWN0aW9uOjI=", - "node": { - "name": "A-Wing" - } - }, - { - "cursor": "YXJyYXljb25uZWN0aW9uOjM=", - "node": { - "name": "Millenium Falcon" - } - }, - { - "cursor": "YXJyYXljb25uZWN0aW9uOjQ=", - "node": { - "name": "Home One" - } - } - ] - } - } -} -``` - -Sweet! Let's keep going and get the next four! - -``` - -query RebelsQuery { - rebels { - name, - ships(first: 4 after: "YXJyYXljb25uZWN0aW9uOjQ=") { - edges { - cursor, - node { - name - } - } - } - } -} -``` - -yields - -```json - -{ - "rebels": { - "name": "Alliance to Restore the Republic", - "ships": { - "edges": [] - } - } -} -``` - -Hm. There were no more ships; guess there were only five in the system for the rebels. It would have been nice to know that we'd reached the end of the connection, without having to do another round trip in order to verify that. The connection model exposes this capability with a type called `PageInfo`. So let's issue the two queries that got us ships again, but this time ask for `hasNextPage`: - -``` - -query EndOfRebelShipsQuery { - rebels { - name, - originalShips: ships(first: 2) { - edges { - node { - name - } - } - pageInfo { - hasNextPage - } - } - moreShips: ships(first: 3 after: "YXJyYXljb25uZWN0aW9uOjE=") { - edges { - node { - name - } - } - pageInfo { - hasNextPage - } - } - } -} -``` - -and we get back - -```json - -{ - "rebels": { - "name": "Alliance to Restore the Republic", - "originalShips": { - "edges": [ - { - "node": { - "name": "X-Wing" - } - }, - { - "node": { - "name": "Y-Wing" - } - } - ], - "pageInfo": { - "hasNextPage": true - } - }, - "moreShips": { - "edges": [ - { - "node": { - "name": "A-Wing" - } - }, - { - "node": { - "name": "Millenium Falcon" - } - }, - { - "node": { - "name": "Home One" - } - } - ], - "pageInfo": { - "hasNextPage": false - } - } - } -} -``` - -So on the first query for ships, GraphQL told us there was a next page, but on the next one, it told us we'd reached the end of the connection. - -Relay uses all of this functionality to build out abstractions around connections, to make these easy to work with efficiently without having to manually manage cursors on the client. - -

Complete details on how the server should behave are available in the GraphQL Cursor Connections spec.

- -## Mutations - -Relay uses a common pattern for mutations, where they are root fields on the mutation type with a single argument, `input`, and where the input and output both contain a client mutation identifier used to reconcile requests and responses. - -By convention, mutations are named as verbs, their inputs are the name with "Input" appended at the end, and they return an object that is the name with "Payload" appended. - -So for our `introduceShip` mutation, we create two types: `IntroduceShipInput` and `IntroduceShipPayload`: - -``` - -input IntroduceShipInput { - factionId: ID! - shipName: String! - clientMutationId: String! -} - -type IntroduceShipPayload { - faction: Faction - ship: Ship - clientMutationId: String! -} -``` - -With this input and payload, we can issue the following mutation: - -``` - -mutation AddBWingQuery($input: IntroduceShipInput!) { - introduceShip(input: $input) { - ship { - id - name - } - faction { - name - } - clientMutationId - } -} -``` - -with these params: - -```json - -{ - "input": { - "shipName": "B-Wing", - "factionId": "1", - "clientMutationId": "abcde" - } -} -``` - -and we'll get this result: - -```json - -{ - "introduceShip": { - "ship": { - "id": "U2hpcDo5", - "name": "B-Wing" - }, - "faction": { - "name": "Alliance to Restore the Republic" - }, - "clientMutationId": "abcde" - } -} -``` - -## Further Reading - -

This concludes the overview of the GraphQL Server Specifications. For the detailed requirements of a Relay-compliant GraphQL server, a more formal description of the Relay cursor connection model and the GraphQL global object identification model are all available.

- -To see code implementing the specification, the [GraphQL.js Relay library](https://github.com/graphql/graphql-relay-js) provides helper functions for creating nodes, connections, and mutations; that repository's [`__tests__`](https://github.com/graphql/graphql-relay-js/tree/main/src/__tests__) folder contains an implementation of the above example as integration tests for the repository. diff --git a/website/versioned_docs/version-v1.4.1/Introduction-InstallationAndSetup.md b/website/versioned_docs/version-v1.4.1/Introduction-InstallationAndSetup.md deleted file mode 100644 index f1b3db0793829..0000000000000 --- a/website/versioned_docs/version-v1.4.1/Introduction-InstallationAndSetup.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -id: installation-and-setup -title: Installation and Setup -original_id: installation-and-setup ---- -## Installation - -Install React and Relay using `yarn` or `npm`: - -```sh - -yarn add react react-dom react-relay - -``` - -## Set up babel-plugin-relay - -Relay Modern requires a Babel plugin to convert GraphQL to runtime artifacts: - -```sh - -yarn add --dev babel-plugin-relay graphql - -``` - -Add `"relay"` to the list of plugins your `.babelrc` file: - -```javascript -{ - "plugins": [ - "relay" - ] -} -``` - -Please note that the `"relay"` plugin should run before other plugins or -presets to ensure the `graphql` template literals are correctly transformed. See -Babel's [documentation on this topic](https://babeljs.io/docs/plugins/#plugin-preset-ordering). - -See the [Migration Setup](./Modern-MigrationSetup.md) guide if upgrading an existing Relay app. - -## Set up relay-compiler - -Relay's ahead-of-time compilation requires the [Relay Compiler](Modern-GraphQLInRelay.md#relay-compiler), which you can install via `yarn` or `npm`: - -```sh - -yarn add --dev relay-compiler graphql - -``` - -This installs the bin script `relay-compiler` in your node_modules folder. It's recommended to run this from a `yarn`/`npm` script by adding a script to your `package.json` file: - -```javascript -"scripts": { - "relay": "relay-compiler --src ./src --schema ./schema.graphql" -} -``` - -Then, after making edits to your application files, just run the `relay` script to generate new compiled artifacts: - -```sh - -yarn run relay - -``` - -Alternatively, you can pass the `--watch` option to watch for file changes in your source code and automatically re-generate the compiled artifacts (**Note:** Requires [watchman](https://facebook.github.io/watchman) to be installed): - -```sh - -yarn run relay -- --watch - -``` - -For more details, check out our [Relay Compiler docs](Modern-GraphQLInRelay.md#relay-compiler). - -## JavaScript environment requirements - -The Relay Modern packages distributed on NPM use the widely-supported ES5 -version of JavaScript to support as many browser environments as possible. - -However, Relay Modern expects modern JavaScript global types (`Map`, `Set`, -`Promise`, `Object.assign`) to be defined. If you support older browsers and -devices which may not yet provide these natively, consider including a global -polyfill in your bundled application, such as [core-js][] or -[babel-polyfill](https://babeljs.io/docs/usage/polyfill/). - -A polyfilled environment for Relay using [core-js][] to support older browsers -might look like: - -```javascript -require('core-js/es6/map'); -require('core-js/es6/set'); -require('core-js/es6/promise'); -require('core-js/es6/object'); - -require('./myRelayApplication'); -``` - -[core-js]: https://github.com/zloirock/core-js diff --git a/website/versioned_docs/version-v1.4.1/Introduction-IntroductionToRelay.md b/website/versioned_docs/version-v1.4.1/Introduction-IntroductionToRelay.md deleted file mode 100644 index 1aa94c1ffeb79..0000000000000 --- a/website/versioned_docs/version-v1.4.1/Introduction-IntroductionToRelay.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -id: introduction-to-relay -title: Introduction to Relay -original_id: introduction-to-relay -slug: / ---- -Relay is a JavaScript framework for building data-driven React applications powered by GraphQL, designed from the ground up to be easy to use, extensible and, most of all, performant. Relay accomplishes this with static queries and ahead-of-time code generation. - -[React](https://facebook.github.io/react/) allows views to be defined as components where every component is responsible for rendering a part of the UI. Composing other components is how to build complex UIs. Each React component doesn't need to know the inner workings of the composed components. - -Relay couples React with GraphQL and develops the idea of encapsulation further. It allows components to specify what data they need and the Relay framework provides the data. This makes the data needs of inner components opaque and allows composition of those needs. Thinking about what data an app needs becomes localized to the component making it easier to reason about what fields are needed or no longer needed. diff --git a/website/versioned_docs/version-v1.4.1/Introduction-Prerequisites.md b/website/versioned_docs/version-v1.4.1/Introduction-Prerequisites.md deleted file mode 100644 index bdb1a72db7fa8..0000000000000 --- a/website/versioned_docs/version-v1.4.1/Introduction-Prerequisites.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -id: prerequisites -title: Prerequisites -original_id: prerequisites ---- -## React - -Relay is a framework for data management with the primary supported binding for React applications, so we assume that you are already familiar with [React](https://reactjs.org/). - -## GraphQL - -We also assume basic understanding of [GraphQL](http://graphql.org/learn/). In order to start using Relay, you will also need: - -### A GraphQL Schema - -A description of your data model with an associated set of resolve methods that know how to fetch any data your application could ever need. - -GraphQL is designed to support a wide range of data access patterns. In order to understand the structure of an application's data, Relay requires that you follow certain conventions when defining your schema. These are documented in the [GraphQL Server Specification](GraphQL-ServerSpecification.md). - -- **[graphql-js](https://github.com/graphql/graphql-js)** on [npm](https://www.npmjs.com/package/graphql) - - General-purpose tools for building a GraphQL schema using JavaScript - -- **[graphql-relay-js](https://github.com/graphql/graphql-relay-js)** on [npm](https://www.npmjs.com/package/graphql-relay) - - JavaScript helpers for defining connections between data, and mutations, in a way that smoothly integrates with Relay. - -### A GraphQL Server - -Any server can be taught to load a schema and speak GraphQL. Our [examples](https://github.com/relayjs/relay-examples) use Express. - -- **[express-graphql](https://github.com/graphql/express-graphql)** on [npm](https://www.npmjs.com/package/express-graphql) -- **[graphql-up](https://github.com/graphcool/graphql-up)** on [npm](https://www.npmjs.com/package/graphql-up) -- **[Graphcool](https://www.graph.cool/)** ([Quickstart tutorial](https://www.graph.cool/docs/quickstart/)) diff --git a/website/versioned_docs/version-v1.4.1/Introduction-QuickStartGuide.md b/website/versioned_docs/version-v1.4.1/Introduction-QuickStartGuide.md deleted file mode 100644 index bfca396ec4914..0000000000000 --- a/website/versioned_docs/version-v1.4.1/Introduction-QuickStartGuide.md +++ /dev/null @@ -1,584 +0,0 @@ ---- -id: quick-start-guide -title: Quick Start Guide -original_id: quick-start-guide ---- -In this guide we are going to give a brief overview of how Relay works and how to use it, using as reference an example todo list app. For more thorough documentation, check out our Guides and API sections. - -Table of Contents: - -- [Setup](#setup) -- [Relay Environment](#relay-environment) -- [Rendering GraphQL Queries](#rendering-graphql-queries) -- [Using Query Variables](#using-query-variables) -- [Using Fragments](#using-fragments) -- [Composing Fragments](#composing-fragments) -- [Rendering Fragments](#rendering-fragments) -- [Mutating Data](#mutating-data) -- [Next Steps](#next-steps) - -## Setup - -Before starting, make sure to check out our [Prerequisites](Introduction-Prerequisites.md) and [Installation and Setup](Introduction-InstallationAndSetup.md) guides. As mentioned in the prerequisites, we need to make sure that we've set up a GraphQL server and schema. - -Fortunately, we are going to be using this [example todo list app](https://github.com/relayjs/relay-examples/tree/main/todo), which already has a [server](https://github.com/relayjs/relay-examples/blob/main/todo/server.js) and schema [schema](https://github.com/relayjs/relay-examples/blob/main/todo/data/schema.graphql) available for us to use: - -```graphql -# From schema.graphql -# https://github.com/relayjs/relay-examples/blob/main/todo/data/schema.graphql - -type Query { - viewer: User - - # Fetches an object given its ID - node( - # The ID of an object - id: ID! - ): Node -} -``` - -Additionally, we will be using [Flow](https://flow.org/) inside our JavaScript code examples. Flow is optional to set up in your project, but we will include it in our examples for completeness. - -## Relay Environment - -Before we can start rendering pixels on the screen, we need to configure Relay via a [Relay Envionment](Modern-RelayEnvironment.md). The environment bundles together the configuration, cache storage, and network-handling that Relay needs in order to operate. - -For the purposes of our example, we are simply going to configure our environment to communicate with our existing GraphQL server: - -```javascript -import { - Environment, - Network, - RecordSource, - Store, -} from 'relay-runtime'; - -function fetchQuery( - operation, - variables, -) { - return fetch('/graphql', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query: operation.text, - variables, - }), - }).then(response => { - return response.json(); - }); -} - -const environment = new Environment({ - network: Network.create(fetchQuery), - store: new Store(new RecordSource()), -}); - -export default environment; -``` - -A Relay Environment requires at least a [Store](Modern-RelayStore.md) and a [Network Layer](Modern-NetworkLayer.md). The above code uses the default implementation for `Store`, and creates a [Network Layer](Modern-NetworkLayer.md) using a simple `fetchQuery` function to fetch a GraphQL query from our server. - -Usually we'd want a single environment in our app, so you could export this environment as a singleton instance from a module to make it accessible across your app. - -## Rendering GraphQL Queries - -Now that we've configured our Relay Environment, we can start fetching queries and rendering data on the screen. The entry point to render data from a GraphQL query is the [`QueryRenderer`](Modern-QueryRenderer.md) component provided by `react-relay`. - -To start, let's assume we just want to render the user id on the screen. From our [schema](https://github.com/relayjs/relay-examples/blob/main/todo/data/schema.graphql#L66), we know that we can get the current `User` via the `viewer` field, so let's write a sample query to fetch the current user id: - -```graphql -query UserQuery { - viewer { - id - } -} -``` - -Now, let's see what it would take to create a component that fetches and renders the above query: - -```javascript -// App.js -import React from 'react'; -import {graphql, QueryRenderer} from 'react-relay'; - -const environment = // defined or imported above... - -export default App extends React.Component { - render() { - return ( - { - if (error) { - return
Error!
; - } - if (!props) { - return
Loading...
; - } - return
User ID: {props.viewer.id}
; - }} - /> - ); - } -} -``` - -Our app is rendering a `QueryRenderer` in the above code, like any other React Component, but let's see what's going on in the props that we are passing to it: - -- We're passing the `environment` we defined earlier. -- We're using using the [`graphql`](Modern-GraphQLInRelay.md) function to define our GraphQL query. `graphql` is a template tag that is never executed at runtime, but rather used by the [Relay Compiler](Modern-GraphQLInRelay.md#relay-compiler) to generate the runtime artifacts that Relay requires to operate. We don't need to worry about this right now; for more details check out our [GraphQL in Relay](Modern-GraphQLInRelay.md) docs. -- We're passing an empty set of `variables`. We'll look into how to use variables in the next section. -- We're passing a `render` function; as you can tell from the code, Relay gives us some information about wether an error occurred, or if we're still fetching the query. If everything succeeds, the data we requested will be available inside `props`, with the same shape as the one specified in the query. - -In order to run this app, we need to first compile our query using the Relay Compiler. Assuming the setup from [Installation and Setup](Introduction-InstallationAndSetup.md), we can just run `yarn relay`. - -For more details on `QueryRenderer`, check out the [docs](Modern-QueryRenderer.md). - -## Using Query Variables - -Let's assume for a moment that in our app we want to be able to view data for different users, so we're going to somehow need to query users by id. From our [schema](https://github.com/relayjs/relay-examples/blob/main/todo/data/schema.graphql#L69), we know we can query nodes given an id, so let's write a parametrized query to get a user by id: - -```graphql -query UserQuery($userID: ID!) { - node(id: $userID) { - id - } -} -``` - -Now, let's see how we would fetch the above query using a `QueryRenderer`: - -```javascript -// UserTodoList.js -// @flow -import React from 'react'; -import PropTypes from 'prop-types'; -import {graphql, QueryRenderer} from 'react-relay'; - -const environment = // defined or imported above... - -type Props = { - userID: string, -}; - -export default UserTodoList extends React.Component { - render() { - const {userID} = this.props; - - return ( - { - if (error) { - return
Error!
; - } - if (!props) { - return
Loading...
; - } - return
User ID: {props.node.id}
; - }} - /> - ); - } -} -``` - -The above code is doing something very similar to our [previous example](#rendering-graphql-queries), however, we are now passing a `$userID` variable to the GraphQL query, via the `variables` prop. This has a couple of important implications: - -- Given that `userID` is also a prop that our component takes, it could receive a new `userID` from its parent component at any moment. When this happens, we new `variables` will be passed down to our `QueryRenderer`, which will automatically cause it to re-fetch the query with the new value for `$userID`. -- The `$userID` variable will now be available anywhere inside that query; this will become important when to keep in mind when using fragments. - -Now that we've updated the query, don't forget to run `yarn relay`. - -## Using Fragments - -Now that we know how to define and fetch queries, let's actually start building a todo list. - -First, let's start at the bottom; let's say that we want to render a component that given a todo item, simply displays the item's text and completed state: - -```javascript -// Todo.js -import React from 'react'; - -type Props = { - todo: { - complete: boolean, - text: string, - }, -}; - -export default class Todo extends React.Component { - render() { - const {complete, text} = this.props.todo; - - return ( -
  • -
    - - -
    -
  • - ); - } -} -``` - -From our [schema](https://github.com/relayjs/relay-examples/blob/main/todo/data/schema.graphql#L107), we know that we can query this data on the `Todo` type. However, we don't want to have to send a separate query for each todo item; that would defeat the purpose of using GraphQL over a traditional REST API. We could manually query for these fields directly in our `QueryRenderer` query, but that would hurt re-usability: what if we want to query the same set of fields as part of a different query? Additionally, we wouldn't know which component needs the data we're querying, a problem Relay directly tries to address. - -Instead, we can define a reusable [Fragment](http://graphql.org/learn/queries/#fragments), which allows us to define a set of fields on a type and reuse them within our queries wherever we need to: - -```graphql -fragment TodoItemFragment on Todo { - complete - text -} -``` - -Our component can then use this fragment to declare its data dependency on the `Todo` GraphQL type: - -```javascript -// Todo.js - -// OPTIONAL: Flow type generated after running `yarn relay`, defining an Object type with shape of the fragment: -import type {Todo_todo} from './__generated__/Todo_todo.graphql'; - -import React from 'react'; -import {graphql, createFragmentContainer} from 'react-relay' - -type Props = { - todo: Todo_todo -} - -class Todo extends React.Component { - render() { - const {complete, text} = this.props.todo; - - return ( -
  • -
    - - -
    -
  • - ); - } -} - -export default createFragmentContainer( - Todo, - graphql` - # As a convention, we name the fragment as '_' - fragment Todo_todo on Todo { - complete - text - } - ` -) - -``` - -The above code highlights one of Relay's most important principles which is colocation of components with their data dependencies. This is beneficial for a few reasons: - -- It becomes obvious at a glance what data is required to render a given component, without having to search which query in our app is fetching the required data. -- As a corollary, the component is de-coupled from the query that renders it. We can change the data dependencies for the component without having to update the queries that render them or worrying about breaking other components. - -Check out our [Thinking in Relay](PrinciplesAndArchitecture-ThinkingInRelay.md) guide for more details behind Relay's principles. - -Before proceeding, don't forget to run the Relay Compiler with `yarn relay`. - -## Composing Fragments - -Given that [Fragment Containers](Modern-FragmentContainer.md) are just React components, we can compose them as such, and even re-use fragment containers within other fragment containers. As an example, let's see how we would define a `TodoList` component that just renders a list of todo items, and whether all have been completed or not: - -```javascript -// TodoList.js - -// OPTIONAL: Flow type generated after running `yarn relay`, defining an Object type with shape of the fragment: -import type {TodoList_userTodoData} from './__generated__/TodoList_userTodoData.graphql'; - -import React from 'react'; -import {graphql, createFragmentContainer} from 'react-relay'; - -type Props = { - userTodoData: TodoList_userTodoData, -} - -class TodoList extends React.Component { - render() { - const {userTodoData: {totalCount, completedCount, todos}} = this.props; - - return ( -
    - -
      - {todos.edges.map(edge => - - )} -
    -
    - ); - } -} - -export default createFragmentContainer( - TodoList, - graphql` - # As a convention, we name the fragment as '_' - fragment TodoList_userTodoData on User { - todos( - first: 2147483647 # max GraphQLInt, to fetch all todos - ) { - edges { - node { - id, - # We use the fragment defined by the child Todo component here - ...Todo_todo, - }, - }, - }, - id, - totalCount, - completedCount, - } - `, -); -``` - -As with the first fragment container we defined, `TodoList` declares it's data dependencies via a fragment. However, this component additionally re-uses the fragment previously defined by the `Todo` component, and passes the appropriate data to when rendering the child `Todo` components (a.k.a. fragment containers). - -One final thing to note when composing fragment containers, is that the parent will not have access to the data defined by the child container, i.e. Relay only allows components to access data they specifically ask for in GraphQL fragments — nothing more. This is called [Data Masking](PrinciplesAndArchitecture-ThinkingInRelay.md#data-masking), and it's intentional to prevent components from depending on data they didn't declare as a dependency. - -## Rendering Fragments - -Now that we have some components (a.k.a fragment containers) that declare their data dependencies, we need to hook them up to a `QueryRenderer` so that the data is actually fetched and rendered; remember, -fragment containers do not directly fetch data. Instead, containers declare a specification of the data needed to render, and Relay guarantees that this data is available before rendering. - -A `QueryRenderer` rendering these fragment containers could look like the following: - -```javascript -// ViewerTodoList.js -import React from 'react'; -import PropTypes from 'prop-types'; -import {graphql, QueryRenderer} from 'react-relay'; -import TodoList from './TodoList' - -const environment = // defined or imported above... - -export default ViewerTodoList extends React.Component { - render() { - return ( - { - if (error) { - return
    Error!
    ; - } - if (!props) { - return
    Loading...
    ; - } - return ( -
    -
    Todo list for User {props.user.id}:
    - -
    - ); - }} - /> - ); - } -} -``` - -Check out or docs for [Fragment Containers](Modern-FragmentContainer.md) for more details, and our guides on [Refetch](Modern-RefetchContainer.md) and [Pagination](Modern-PaginationContainer.md) for more advanced usage of containers. - -## Mutating Data - -Now that we know how to query for and render data, let's move on to changing our data. We know that to change any data in our server, we need to use GraphQL [Mutations](http://graphql.org/learn/queries/#mutations). - -From our [schema](https://github.com/relayjs/relay-examples/blob/main/todo/data/schema.graphql#L35), we know that we have some mutations available to us, so let's start by writing a mutation to change the `complete` status of a given todo item (i.e. mark or unmark it as done): - -```graphql -mutation ChangeTodoStatusMutation($input: ChangeTodoStatusInput!) { - changeTodoStatus(input: $input) { - todo { - id - complete - } - } -} -``` - -This mutation allows us to query back some data as a [result of the mutation](https://github.com/relayjs/relay-examples/blob/main/todo/data/schema.graphql#L18), so we're going to query for the updated `complete` status on the todo item. - -In order to execute this mutation in Relay, we're going to write a new mutation using Relay's `commitMutation` api: - -```javascript -// ChangeTodoStatusMutation.js -import {graphql, commitMutation} from 'react-relay'; - -// We start by defining our mutation from above using `graphql` -const mutation = graphql` - mutation ChangeTodoStatusMutation($input: ChangeTodoStatusInput!) { - changeTodoStatus(input: $input) { - todo { - id - complete - } - } - } -`; - -function commit( - environment, - complete, - todo, -) { - // Now we just call commitMutation with the appropriate parameters - return commitMutation( - environment, - { - mutation, - variables: { - input: {complete, id: todo.id}, - }, - } - ); -} - -export default {commit}; -``` - -Whenever we call `ChangeTodoStatusMutation.commit(...)`, Relay will send the mutation to the server, and in our case, upon receiving a response it will automatically update the local data store with the latest data from the server. This also means that upon receiving the response, Relay will ensure that any components (i.e. containers) that depend on the updated data are re-rendered. - -In order to actually use this mutation in our component, we could update our `Todo` component in the following way: - -```javascript -// Todo.js - -// ... - -class Todo extends React.Component { - // Add a new event handler that fires off the mutation - _handleOnCheckboxChange = (e) => { - const complete = e.target.checked; - ChangeTodoStatusMutation.commit( - this.props.relay.environment, - complete, - this.props.todo, - ); - }; - - render() { - // ... - } -} - -// ... - -``` - -### Optimistic Updates - -In our example above, the `complete` status in our component won't be updated and re-rendered until we get a response back from the server, which won't make for a great user experience. - -In order to make the experience better, we can configure our mutation to do an optimistic update. An optimistic update means immediately updating our local data with what we expect it to be if we get a successful response from the server, i.e. updating the data immediately assuming that the mutation request will succeed. If the request doesn't succeed, we can roll-back our update. - -In Relay, there's a couple of options we can pass to `commitMutation` to enable optimistic updates. Let's see what that would look like in our `ChangeTodoStatusMutation`: - -```javascript -// ChangeTodoStatusMutation.js - -// ... - -function getOptimisticResponse(complete, todo) { - return { - changeTodoStatus: { - todo: { - complete: complete, - id: todo.id, - }, - }, - }; -} - -function commit( - environment, - complete, - todo -) { - // Now we just call commitMutation with the appropriate parameters - return commitMutation( - environment, - { - mutation, - variables: { - input: {complete, id: todo.id}, - }, - optimisticResponse: getOptimisticResponse(complete, todo), - } - ); -} - -export default {commit}; -``` - -In the simplest case above, we just need to pass an `optimisticResponse` option, which should refer to an object having the same shape as the mutation response payload. When we pass this option, Relay will know to immediately update our local data with the optimistic response, and then update it with the actual server response, or roll it back if an error occurs. - -### Updating local data from mutation responses - -By default, Relay will know to update the fields on the records referenced by the mutation payload, (i.e. the `todo` in our example). However, this is only the simplest case, and in some cases updating the local data isn't as simple as just updating the fields in a record. - -For instance, we might be updating a collection of items, or we might be deleting a record entirely. For these more advanced scenarios, Relay allows us to pass a set of options for us to control how we update the local data from a server response, including a set of [`configs`](Modern-Mutations.md#configs), and an [`updater`](Modern-Mutations.md#updating-the-store-programatically-advanced) function for full control over the update. - -For more details and advanced use cases on mutations and updates, check out our [Mutations](Modern-Mutations.md) docs. - -## Next Steps - -This guide just scratches the surface of Relay's API; for more detailed docs and guides, check out our API Reference and Guides sections. diff --git a/website/versioned_docs/version-v1.4.1/Modern-APICheatsheet.md b/website/versioned_docs/version-v1.4.1/Modern-APICheatsheet.md deleted file mode 100644 index e88b60f4485ca..0000000000000 --- a/website/versioned_docs/version-v1.4.1/Modern-APICheatsheet.md +++ /dev/null @@ -1,56 +0,0 @@ ---- -id: api-cheatsheet -title: API Cheatsheet -original_id: api-cheatsheet ---- -A reference for translating between the Relay Classic and Relay Modern APIs. - -### To add a new root for relay components - -Classic: `` - -Modern: `` - -### To add a new relay container - -Classic: `Relay.createContainer` - -Modern: `createFragmentContainer` - -### To add a new relay container that has changing data requirements - -Classic: `Relay.createContainer` - -Modern `createRefetchContainer` - -### To add a new paginating relay container - -Classic: `Relay.createContainer` - -Modern: `createPaginationContainer` - -### To update a variable for my component - -Classic: `this.props.relay.setVariable({foo: bar}...)` - -Modern: `this.props.relay.refetch({foo: bar}...` in a Refetch Container - -### To paginate through a connection - -Classic: `this.props.relay.setVariable({count: prevCount + pageSize}...)` - -Modern `this.props.relay.loadMore(pageSize...)` in a Pagination Container - -### To force fetch a component - -Classic: `this.props.relay.forceFetch()` - -Modern: `this.props.relay.refetchConnection(...)` in a Pagination Container - -or: `this.props.relay.refetch({}, {}, callback, {force: true})` in a Refetch Container - -### To commit a mutation - -Classic: `this.props.relay.commitUpdate(mutation...)` - -Modern: `commitMutation(this.props.relay.environment, {mutation...})` diff --git a/website/versioned_docs/version-v1.4.1/Modern-CompatibilityCheatsheet.md b/website/versioned_docs/version-v1.4.1/Modern-CompatibilityCheatsheet.md deleted file mode 100644 index eb53fbd262ea5..0000000000000 --- a/website/versioned_docs/version-v1.4.1/Modern-CompatibilityCheatsheet.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -id: compatibility-cheatsheet -title: Compatibility Cheatsheet -original_id: compatibility-cheatsheet ---- -What works with what? Relay Compat (`'react-relay/compat'`) is the most flexible. -Compat components and mutations can be used by everything. Compat components can also have any kind of children. - -However components using the Relay Modern API (`'react-relay'`) and the Relay Classic API (`'react-relay/classic'`) cannot be used with each other. - -### Can RelayRootContainer use: - -| Classic Component | Compat Component | Modern Component | Classic Mutation | Compat Mutation | Modern Mutation | -| ----------------- | ---------------- | ---------------- | ---------------- | --------------- | --------------- | -| Yes | Yes | No | Yes | Yes | No | - -### Can QueryRenderer using Classic Environment (`Store` in `react-relay/classic`) use: - -| Classic Component | Compat Component | Modern Component | Classic Mutation | Compat Mutation | Modern Mutation | -| ----------------- | ---------------- | ---------------- | ---------------- | --------------- | --------------- | -| Yes | Yes | No | Yes | Yes | No | - -### Can QueryRenderer using Modern Environment use: - -| Classic Component | Compat Component | Modern Component | Classic Mutation | Compat Mutation | Modern Mutation | -| ----------------- | ---------------- | ---------------- | ---------------- | --------------- | --------------- | -| No | Yes | Yes | No | Yes | Yes | - -### Can React Modern Component use: - -| Classic Component | Compat Component | Modern Component | Classic Mutation | Compat Mutation | Modern Mutation | -| ----------------- | ---------------- | ---------------- | ---------------- | --------------- | --------------- | -| No | Yes | Yes | No | Yes | Yes | - -### Can React Compat Component use: - -| Classic Component | Compat Component | Modern Component | Classic Mutation | Compat Mutation | Modern Mutation | -| ----------------- | ---------------- | ---------------- | ---------------- | --------------- | --------------- | -| Yes | Yes | Yes | Yes\* | Yes | Yes | - -\* Modern API doesn't support mutation fragments. You might have to inline the mutation fragments from your legacy mutation in the fragment of the component. - -### Can React Classic Component use: - -| Classic Component | Compat Component | Modern Component | Classic Mutation | Compat Mutation | Modern Mutation | -| ----------------- | ---------------- | ---------------- | ---------------- | --------------- | --------------- | -| Yes | Yes | No | Yes | Yes | No | diff --git a/website/versioned_docs/version-v1.4.1/Modern-ConversionPlaybook.md b/website/versioned_docs/version-v1.4.1/Modern-ConversionPlaybook.md deleted file mode 100644 index be67ed81baee2..0000000000000 --- a/website/versioned_docs/version-v1.4.1/Modern-ConversionPlaybook.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -id: conversion-playbook -title: Conversion Playbook -original_id: conversion-playbook ---- -Incrementally modernize your Relay Classic app in these steps: - -## Step 0: Install and configure your environment - -Follow the steps outlined in the [Migration Setup](./Modern-MigrationSetup.md) guide. - -## Step 1: Incrementally convert to Relay Compat - -Start converting your components and mutations to use the Relay Modern APIs from the `'react-relay/compat'` module (`createFragmentContainer`, `createRefetchContainer`, `createPaginationContainer`, `commitMutation`). It will be easier to go from the leaf components up. The [conversion scripts](https://github.com/relayjs/relay-codemod) should make this step less tedious. - -If a fragment uses variables that are determined at runtime, [see below](#note-determining-variable-values-at-runtime). - -## Step 2: Introduce `` - -Once all the components and mutations have been converted to use the Relay Modern APIs, convert to using `QueryRenderer` instead of using `Relay.Renderer` or `Relay.RootContainer`. You may supply `Store` from `'react-relay/classic'` as the `environment` for most cases. - -## Step 3: Introduce Relay Modern runtime - -Once a few or all of your views are using `QueryRenderer`, `Store` from `'react-relay/classic'` could be replaced with a `RelayModernEnvironment`. Keep in mind that `RelayModernEnvironment` and `Store` do not share any data. You might want to hold off on this step until views that have significant data overlap can be switched over at the same time. This step is what unlocks the perf wins for your app. Apps using the `RelayModernEnvironment` get to send persisted query IDs instead of the full query strings to the server, as well as much more optimized data normalizing and processing. - -## Step 4: Clean up by replacing Relay Compat with Relay Modern. - -Switch the `'react-relay/compat'` references in your app to `'react-relay'`. This is more of a clean-up step that prevents your app from pulling in unnecessary `'react-relay/classic'` code. - -## Note: Determining variable values at runtime - -There is currently only one supported way to set the initial value of a variable dynamically: using global variables defined on the query that includes the fragment (or via `variables` on the `QueryRenderer`). - -For example, if `currentDate` is set in `QueryRenderer` `variables`, then $currentDate may be referenced in any fragment included in the `QueryRenderer` `query`. - -If you're using `createRefetchContainer` then your `refetch` method may also update these variables to render with new values. diff --git a/website/versioned_docs/version-v1.4.1/Modern-ConversionScripts.md b/website/versioned_docs/version-v1.4.1/Modern-ConversionScripts.md deleted file mode 100644 index 3a10abc594249..0000000000000 --- a/website/versioned_docs/version-v1.4.1/Modern-ConversionScripts.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -id: conversion-scripts -title: Conversion Scripts -original_id: conversion-scripts ---- -We built a few scripts to help you with the conversion process. Check them out at [github.com/relayjs/relay-codemod](https://github.com/relayjs/relay-codemod). diff --git a/website/versioned_docs/version-v1.4.1/Modern-ConvertingMutations.md b/website/versioned_docs/version-v1.4.1/Modern-ConvertingMutations.md deleted file mode 100644 index 0064ece749d5a..0000000000000 --- a/website/versioned_docs/version-v1.4.1/Modern-ConvertingMutations.md +++ /dev/null @@ -1,168 +0,0 @@ ---- -id: converting-mutations -title: Converting Mutations -original_id: converting-mutations ---- -We made some changes to how mutations work in the new version of Relay in order to makes them more straight forward to use and more customizable. Mutations are currently not covered by an automatic conversion and require a manual upgrade. However, limited number of changes is needed to make your existing mutations work with both the old and new environment. - -## Simplifying FatQueries to Standard GraphQL Queries - -FatQueries in Relay Classic mutations was a concept that was confusing for a number of people. It required Relay to keep track of a significant amount of metadata regarding each record and automatically figure out the query to send to the server for the mutation. The logic to deduce the queries to send to the server was both complicated to maintain and slow to run. On top of that, we often had questions about why a particular field is included or skipped. We decided to allow people to have more control by allowing them write out exactly what data they want to update as the result of a mutation. Both individual fields and fragments can be included in these queries. Similar to container fragments, this is subjected to masking. That means only fields listed out directly will be accessible in the callbacks and the updater functions. The data fetched by in referenced fragments will still be updated in the store. - -Example of existing fat query: - -```javascript - RelayClassic.QL` - fragment on MarkReadNotificationResponsePayload @relay(pattern: true) { - notification - } - `; -``` - -Example of converted mutation query: - -```javascript -graphql` - mutation MarkReadNotificationMutation( - $input: MarkReadNotificationData! - ) { - markReadNotification(data: $input) { - notification { - seenState # include fields to be updated - ... MyNotificationComponent_notification # reuse fragments from components to be updated - } - } - } -`; -``` - -## Migrating Configs - -### FIELDS_CHANGE - -This is no longer needed in Compatibility Mode for neither environments. Relay will normalized the data using the mutation query and id to update the store automatically. You can remove it completely. - -### RANGE_ADD - -`RANGE_ADD` needs one additional property in the config named `connectionInfo` to work with the new environment. Learn more about `connectionInfo` [Mutation/RANGE_ADD](./Modern-Mutations.md#range-add) - -### RANGE_DELETE - -`RANGE_DELETE` needs one additional property in the config named `connectionKeys` to work with the new environment. Learn more about `connectionKeys` [Mutation/RANGE_DELETE](./Modern-Mutations.md#range-delete) - -### NODE_DELETE - -`NODE_DELETE` config will work as-is with the new environment. No change is needed. - -## Converting a Simple Mutation - -Take this example of a simple mutation in Relay Classic: - -```javascript -class LikeStoryMutation extends RelayClassic.Mutation { - getMutation() { - return RelayClassic.QL`mutation {likeStory}`; - } - - getVariables() { - return {storyID: this.props.story.id}; - } - - getFatQuery() { - return RelayClassic.QL` - fragment on LikeStoryPayload @relay(pattern: true) { - story { - likers, - likeSentence, - viewerDoesLike, - }, - } - `; - } - - getConfigs() { - return [{ - type: 'FIELDS_CHANGE', - fieldIDs: { - story: this.props.story.id, - }, - }]; - } - - static fragments = { - story: () => Relay.QL` - fragment on Story { - id - } - `, - }; -} -``` - -### Converting `getMutation()` and `getFatQuery()` - -We combine these two into a regular GraphQL mutation, which list out specific fields that needs to be updated. - -```javascript -const mutation = graphql` - mutation LikeStoryMutation($input: LikeStoryData!) { - story(data: $input) { - likers { - count - } - likeSentence - viewerDoesLike - } - } -`; -``` - -### Converting `getConfigs()` - -As specified above, `FIELDS_CHANGE` configs can be omitted. - -### Converting `getVariables()` - -To convert `getVariables()`, we take the return value from the original function and wrap it in an object that contains a property that matches the variable name for the mutation. In this case, the mutation has a `input` variable that is of type `LikeStoryData`. - -```javascript -const variables = { - input: { - storyID: args.storyID - } -} -``` - -### Final Result - -As you can see, our resulting mutation is a lot simpler and more like regular GraphQL than the Relay Classic version we started out with. - -```javascript -const mutation = graphql` - mutation LikeStoryMutation($input: LikeStoryData!) { - story { - likers { - count - }, - likeSentence, - viewerDoesLike - } - } -`; - -// environment should be passed in from your component as this.props.relay.environment -function commit(environment: CompatEnvironment, args) { - const variables = { - input: { - storyID: args.storyID - } - }; - - return commitMutation(environment, { - mutation, - variables, - }); -} -``` - -See [Mutation](./Modern-Mutations.md) for additional options on `commitMutation` for more complex mutations. diff --git a/website/versioned_docs/version-v1.4.1/Modern-Debugging.md b/website/versioned_docs/version-v1.4.1/Modern-Debugging.md deleted file mode 100644 index acacf7e19f61e..0000000000000 --- a/website/versioned_docs/version-v1.4.1/Modern-Debugging.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -id: relay-debugging -title: Debugging -original_id: relay-debugging ---- -When problems arise developers would need an insight into Relay's store. Relay provides a couple of tools to inspect the store and its records. - -Relay DevTools is tool designed to help developers inspect their Relay state and understand how store changes overtime. Relay DevTools ships in two ways: - -- [Chrome Extension][extension] creates a Relay tab in the developer tools interface for debugging apps in Chrome -- [Electron App][app] that connects to React Native apps running Relay - -![Store Explorer](/img/docs/store-explorer.png) -![Mutations View](/img/docs/mutations-view.png) - -[extension]: https://chrome.google.com/webstore/detail/relay-devtools/oppikflppfjfdpjimpdadhelffjpciba - -[app]: https://www.npmjs.com/package/relay-devtools diff --git a/website/versioned_docs/version-v1.4.1/Modern-FragmentContainer.md b/website/versioned_docs/version-v1.4.1/Modern-FragmentContainer.md deleted file mode 100644 index c93f04999c522..0000000000000 --- a/website/versioned_docs/version-v1.4.1/Modern-FragmentContainer.md +++ /dev/null @@ -1,296 +0,0 @@ ---- -id: fragment-container -title: Fragment Container -original_id: fragment-container ---- -A Fragment Container is a [higher-order component](https://reactjs.org/docs/higher-order-components.html) that allows components to specify their data requirements. A container does not directly fetch data, but instead declares a _specification_ of the data needed for rendering, and then Relay will guarantee that this data is available _before_ rendering occurs. - -Table of Contents: - -- [`createFragmentContainer`](#createfragmentcontainer) -- [Example](#example) -- [Container Composition](#container-composition) -- [Rendering Containers](#rendering-containers) - -## `createFragmentContainer` - -`createFragmentContainer` has the following signature: - -```javascript -createFragmentContainer( - component: ReactComponentClass, - fragmentSpec: GraphQLTaggedNode | {[string]: GraphQLTaggedNode}, -): ReactComponentClass; -``` - -### Arguments - -- `component`: The React Component _class_ of the component requiring the fragment data. -- `fragmentSpec`: Specifies the data requirements for the Component via a GraphQL fragment. The required data will be available on the component as props that match the shape of the provided fragment. `fragmentSpec` can be one of 2 things: - - A `graphql` tagged fragment. If the fragment uses the name convention `<...>_`, the fragment's data will be available to the Component as a prop with the given ``. - If the fragment name doesn't specify a prop name, the data will be available as a `data` prop. - - An object whose keys are prop names and values are `graphql` tagged fragments. Each key specified in this object will correspond to a prop in the resulting Component. - - **Note:** To enable [compatibility mode](Modern-RelayCompat.md), `relay-compiler` enforces fragments to be named as `_`. - -### Available Props - -The Component resulting from `createFragmentContainer` will receive the following `props`: - -``` - -type Props = { - relay: { - environment: Environment, - }, - // Additional props as specified by the fragmentSpec -} -``` - -- `relay`: - - `environment`: The current [Relay Environment](Modern-RelayEnvironment.md) - -## Example - -To start, let's build the plain React version of a hypothetical `` component that displays the text and completion status of a `Todo`. - -### React Component - -Here's a basic implementation of `` that ignores styling in order to highlight the functionality: - -```javascript -// TodoItem.js -class TodoItem extends React.Component { - render() { - // Expects the `item` prop to have the following shape: - // { - // item: { - // text, - // isComplete - // } - // } - const item = this.props.item; - return ( - - - {item.text} - - ); - } -} -``` - -### Data Dependencies With GraphQL - -In Relay, data dependencies are described using [GraphQL](https://github.com/facebook/graphql). For ``, the dependency can be expressed as follows. Note that this exactly matches the shape that the component expected for the `item` prop. - -```javascript -graphql` - # This fragment only applies to objects of type 'Todo'. - fragment TodoItem_item on Todo { - text - isComplete - } -` - -``` - -### Defining Containers - -Given the plain React component and a GraphQL fragment, we can now define a Fragment Container to specify this component's data requirements. Let's look at the code first and then see what's happening: - -```javascript -// TodoItem.js -import {createFragmentContainer, graphql} from 'react-relay'; - -class TodoItem extends React.Component // as above - -// Export a *new* React component that wraps the original ``. -export default createFragmentContainer(TodoItem, { - // For each of the props that depend on server data, we define a corresponding - // key in this object. Here, the component expects server data to populate the - // `item` prop, so we'll specify the fragment from above at the `item` key. - item: graphql` - fragment TodoItem_item on Todo { - text - isComplete - } - `, -}); -``` - -Relay will infer the prop name from the fragment name according to the fragment naming convention `<...>_`. The example below is equivalent to the one above: - -```javascript -export default createFragmentContainer( - TodoItem, - graphql` - fragment TodoItem_item on Todo { - text - isComplete - } - `, -); -``` - -If there is no `_` suffix, the `data` prop name will be used: - -```javascript -class TodoItem extends React.Component { - render() { - const item = this.props.data; - // ... - } -} - -export default createFragmentContainer( - TodoItem, - graphql` - fragment TodoItem on Todo { - text - isComplete - } - `, -); -``` - -## Container Composition - -React and Relay support creating arbitrarily complex applications through _composition_. Larger components can be created by composing smaller components, helping us to create modular, robust applications. - -Let's explore how this works via a `` component that composes the `` we defined above. - -### Composing Views - -View composition is _exactly_ what you're used to — Relay containers are just standard React components. Here's the `` component: - -```javascript -class TodoList extends React.Component { - render() { - // Expects a `list` with a string `title`, as well as the information - // for the ``s (we'll get that next). - const list = this.props.list; - // It works just like a React component, because it is one! - return ( - - {list.title} - {list.todoItems.map(item => )} - - ); - } -} -``` - -### Composing Fragments - -Fragment composition works similarly — a parent container's fragment composes the fragment for each of its children. In this case, `` needs to fetch information about the `Todo`s that are required by ``. - -```javascript -class TodoList extends React.Component // as above - -export default createFragmentContainer( - TodoList, - // This `_list` fragment name suffix corresponds to the prop named `list` that - // is expected to be populated with server data by the `` component. - graphql` - fragment TodoList_list on TodoList { - # Specify any fields required by '' itself. - title - # Include a reference to the fragment from the child component. - todoItems { - ...TodoItem_item - } - } - `, -); -``` - -Note that when composing fragments, the type of the composed fragment must match the field on the parent in which it is embedded. For example, it wouldn't make sense to embed a fragment of type `Story` into a parent's field of type `User`. Relay and GraphQL will provide helpful error messages if you get this wrong (and if they aren't helpful, let us know!). - -### Passing Arguments to a Fragment - -#### `@argumentDefinitions` - -When defining a fragment, you can use the [`@argumentDefinitions`](Modern-GraphQLInRelay.md#argumentdefinitions) directive to specify any arguments, with potentially default values, that the fragment expects. - -For example, let's redefine our `TodoList_list` fragment to take some arguments using `@argumentDefinitions`: - -```graphql -fragment TodoList_list on TodoList @argumentDefinitions( - count: {type: "Int", defaultValue: 10}, # Optional argument - userID: {type: "ID"}, # Required argument -) { - title - todoItems(userID: $userID, first: $count) { # Use fragment arguments here as variables - ...TodoItem_item - } -} -``` - -Any arguments defined inside `@argumentDefinitions` will be local variables available inside the fragment's scope. However, a fragment can also reference global variables that were defined in the root query. - -#### `@arguments` - -In order to pass arguments to a fragment that has `@argumentDefinitions`, you need to use the [`@arguments`](Modern-GraphQLInRelay.md#arguments) directive. - -Following our `TodoList_list` example, we would pass arguments to the fragment like so: - -```graphql -query TodoListQuery($count: Int, $userID: ID) { - ...TodoList_list @arguments(count: $count, userID: $userID) # Pass arguments here -} -``` - -### Calling Component Instance Methods - -React component classes may have methods, often accessed via [refs](https://facebook.github.io/react/docs/refs-and-the-dom.html). -Since Relay composes these component instances in a container, you need to use the `componentRef` prop to access them: - -Consider an input with a server-defined placeholder text and an imperative method to focus the input node: - -```javascript -export default createFragmentContainer( - class TodoInput extends React.Component { - focus() { - this.input.focus(); - } - - render() { - return { this.input = node; }} - placeholder={this.props.data.suggestedNextTitle} - />; - } - }, - graphql` - fragment TodoInput on TodoList { - suggestedNextTitle - } - `, -); -``` - -To call this method on the underlying component, first provide a `componentRef` function to the Relay container. This differs from providing a [`ref`](https://facebook.github.io/react/docs/refs-and-the-dom.html) function which would provide a reference to the Relay container itself, not the underlying React Component. - -```javascript -export default createFragmentContainer( - class TodoListView extends React.Component { - render() { - return
    this.input.focus()}> - { this.input = ref; }} - /> -
    ; - } - }, - graphql` - fragment TodoListView on TodoList { - ...TodoInput - } - `, -); -``` - -## Rendering Containers - -As we've learned, Relay fragment containers only declare data requirements as GraphQL fragments. In order to actually fetch and render the specified data, we need to use a `QueryRenderer` component to render a root query and any fragment containers included within. Please refer to our [`QueryRenderer`](Modern-QueryRenderer.md) docs for more details. diff --git a/website/versioned_docs/version-v1.4.1/Modern-GraphQLInRelay.md b/website/versioned_docs/version-v1.4.1/Modern-GraphQLInRelay.md deleted file mode 100644 index e0a6845dc6a6d..0000000000000 --- a/website/versioned_docs/version-v1.4.1/Modern-GraphQLInRelay.md +++ /dev/null @@ -1,241 +0,0 @@ ---- -id: graphql-in-relay -title: GraphQL in Relay -original_id: graphql-in-relay ---- -Table of Contents: - -- [`graphql`](#graphql) -- [Directives](#directives) -- [Relay Compiler](#relay-compiler) - -## `graphql` - -The `graphql` template tag provided by Relay serves as the mechanism to write queries, fragments, mutations or subscriptions in the [GraphQL](http://graphql.org/learn/) language. For example: - -```javascript -import {graphql} from 'react-relay'; - -graphql` - query MyQuery { - viewer { - id - } - } -`; -``` - -The result of using the `graphql` template tag are `GraphQLTaggedNode`s, which are used to define [Query Renderers](Modern-QueryRenderer.md), [Fragment Containers](Modern-FragmentContainer.md), [Refetch Containers](Modern-RefetchContainer.md), [Pagination Containers](Modern-PaginationContainer.md), etc. - -However, `graphql` template tags are **never executed at runtime**. Instead, they are compiled ahead of time by the [Relay Compiler](#relay-compiler) into generated artifacts that live alongside your source code, and which Relay requires to operate at runtime. The [Relay Babel plugin](Introduction-InstallationAndSetup.md#setup-babel-plugin-relay) will then convert the `graphql` literals in your code into `require()` calls for the generated files. - -## Directives - -Relay uses directives to add additional information to GraphQL documents, which are used by the [Relay Compiler](#relay-compiler) to generate the appropriate runtime artifacts. These directives only appear in your application code and are removed from requests sent to your GraphQL server. - -**Note:** The relay-compiler will maintain any directives supported by your server (such as `@include` or `@skip`) so they remain part of the request to the GraphQL server and won't alter generated runtime artifacts. - -### `@arguments` - -`@arguments` is a directive used to pass arguments to a fragment that was defined using [`@argumentDefinitions`](#argumentdefinitions). For example: - -```graphql -query TodoListQuery($userID: ID) { - ...TodoList_list @arguments(count: $count, userID: $userID) # Pass arguments here -} -``` - -See [Fragment Container docs](Modern-FragmentContainer.md#passing-arguments-to-a-fragment) for more details. - -### `@argumentDefinitions` - -`@argumentDefinitions` is a directive used to specify arguments taken by a fragment. For example: - -```graphql -fragment TodoList_list on TodoList @argumentDefinitions( - count: {type: "Int", defaultValue: 10}, # Optional argument - userID: {type: "ID"}, # Required argument -) { - title - todoItems(userID: $userID, first: $count) { # Use fragment arguments here as variables - ...TodoItem_item - } -} -``` - -See [Fragment Container docs](Modern-FragmentContainer.md#passing-arguments-to-a-fragment) for more details. - -### `@connection(key: String!, filters: [String])` - -When using the [Pagination Container](Modern-PaginationContainer.md), Relay expects connection fields to be annotated with a `@connection` directive. For more detailed information and example, check out our docs on using `@connection` inside a Pagination Container [`here`](Modern-PaginationContainer.md#connection). - -**Note:** `@connection` is also supported in [compatibility mode](Modern-RelayCompat.md) - -### `@relay(plural: Boolean)` - -When defining a fragment, you can use the `@relay(plural: true)` directive to indicate that the fragment is backed by a [GraphQL list](http://graphql.org/learn/schema/#lists-and-non-null), meaning that it will inform Relay that this particular field is an array. For example: - -```javascript -graphql` -fragment TodoItems_items on TodoItem @relay(plural: true) { - id - text -}`; -``` - -### `@relay(mask: Boolean)` - -Relay by default will only expose the data for fields explicitly requested by a [component's fragment](Modern-FragmentContainer.md#createfragmentcontainer), which is known as [data masking](PrinciplesAndArchitecture-ThinkingInRelay.md#data-masking). - -However, `@relay(mask: false)` can be used to prevent data masking; when including a fragment and annotating it with `@relay(mask: false)`, its data will be available to the parent, recursively including the data from the fields of the referenced fragment. - -This may be helpful to reduce redundant fragments when dealing with nested or recursive data within a single Component. - -Keep in mind that it is typically considered an **anti-pattern** to create a single fragment shared across many containers. Abusing this directive could result in over-fetching in your application. - -In the example below, the `user` prop will include the data for `id` and `name` fields wherever `...Component_internUser` is included, instead of Relay's normal behavior to mask those fields: - -```javascript -graphql` - fragment Component_internUser on InternUser { - id - name - } -`; - -export default createFragmentContainer( - ({ user }) => /* ... */, - graphql` - fragment Component_user on User { - internUser { - manager { - ...Component_internUser @relay(mask: false) - } - .... on Employee { - admins { - ...Component_internUser @relay(mask: false) - } - reports { - ...Component_internUser @relay(mask: false) - } - } - } - } - `, -); -``` - -## Relay Compiler - -Relay uses the Relay Compiler to convert [`graphql`](#graphql) literals into generated files that live alongside your source files. - -A query like the following: - -```javascript -graphql` - fragment MyComponent on Type { - field - } -` - -``` - -Will cause a generated file to appear in `./__generated__/MyComponent.graphql`, -with both runtime artifacts (which help to read and write from the Relay Store) -and [Flow types](https://flow.org/) to help you write type-safe code. - -The Relay Compiler is responsible for generating code as part of a build step which, at runtime, can be used statically. By building the query ahead of time, the client's JS runtime is not responsible for generating a query string, and fields that are duplicated in the query can be merged during the build step, to improve parsing efficiency. If you have the ability to persist queries to your server, the compiler's code generation process provides a convenient time to convert a query or mutation's text into a unique identifier, which can greatly reduce the upload bytes required in some applications. - -### Set up relay-compiler - -See our relay-compiler section in our [Installation and Setup guide](Introduction-InstallationAndSetup.md#set-up-relay-compiler). - -### GraphQL Schema - -To use the Relay Compiler, you need either a .graphql or .json GraphQL schema file, describing your GraphQL server's API. Typically these files are local representations of a server source of truth and are not edited directly. For example, we might have a `schema.graphql` like: - -```graphql -schema { - query: Root -} - -type Root { - dictionary: [Word] -} - -type Word { - id: String! - definition: WordDefinition -} - -type WordDefinition { - text: String - image: String -} -``` - -### Source files - -Additionally, you need a directory containing `.js` files that use the `graphql` tag to describe GraphQL queries and fragments. Let's call this `./src`. - -Then run `yarn run relay` as set up before. - -This will create a series of `__generated__` directories that are co-located with the corresponding files containing `graphql` tags. - -For example, given the two files: - -- `src/Components/DictionaryComponent.js` - - ```javascript - const DictionaryWordFragment = graphql` - fragment DictionaryComponent_word on Word { - id - definition { - ...DictionaryComponent_definition - } - } - ` - - const DictionaryDefinitionFragment = graphql` - fragment DictionaryComponent_definition on WordDefinition { - text - image - } - ` - - ``` - -- `src/Queries/DictionaryQuery.js` - - ```javascript - const DictionaryQuery = graphql` - query DictionaryQuery { - dictionary { - ...DictionaryComponent_word - } - } - ` - - ``` - -This would produce three generated files, and two `__generated__` directories: - -- `src/Components/__generated__/DictionaryComponent_word.graphql.js` -- `src/Components/__generated__/DictionaryComponent_definition.graphql.js` -- `src/Queries/__generated__/DictionaryQuery.graphql.js` - -### Importing generated definitions - -Typically you will not need to import your generated definitions. The [Relay Babel plugin](Introduction-InstallationAndSetup.md#setup-babel-plugin-relay) will then convert the `graphql` literals in your code into `require()` calls for the generated files. - -However the Relay Compiler also automatically generates [Flow](https://flow.org) types as [type comments](https://flow.org/en/docs/types/comments/). For example, you can import the generated flow types like so: - -```javascript -import type {DictionaryComponent_word} from './__generated__/DictionaryComponent_word.graphql'; -``` - -### Advanced usage - -In addition to the bin script, the `relay-compiler` package also [exports library code](https://github.com/facebook/relay/blob/main/packages/relay-compiler/RelayCompilerPublic.js) which you may use to create more complex configurations for the compiler, or to extend the compiler with your own custom output. - -If you find you need to do something unique (like generate types that conform to an older version of flow, or to parse non-javascript source files), you can build your own version of the Compiler by swapping in your own `FileWriter` and `ASTCache`, or by adding on an additional `IRTransform`. Note, the internal APIs of the `RelayCompiler` are under constant iteration, so rolling your own version may lead to incompatibilities with future releases. diff --git a/website/versioned_docs/version-v1.4.1/Modern-LocalStateManagement.md b/website/versioned_docs/version-v1.4.1/Modern-LocalStateManagement.md deleted file mode 100644 index f341a23cd201d..0000000000000 --- a/website/versioned_docs/version-v1.4.1/Modern-LocalStateManagement.md +++ /dev/null @@ -1,212 +0,0 @@ ---- -id: local-state-management -title: Local State Management ---- -Relay can be used to read and write local data, and act as a single source of truth for _all_ data in your client application. -The Relay Compiler fully supports client-side extensions of the schema, which allows you to define local fields and types. - -Table of Contents: - -- [Extending the server schema](#extending-the-server-schema) -- [Querying local state](#querying-local-state) -- [Mutating local state](#mutating-local-state) -- [Initial local state](#initial-local-state) - -## Extending the server schema - -To extend the server schema, create a new `.graphql` file inside your `--src` directory. -Let's call it `./src/clientSchema.graphql`. - -This schema describes what local data can be queried on the client. -It can even be used to extend an existing server schema. - -For example, we can create a new type called `Note`: - -```graphql -type Note { - id: ID! - title: String - body: String -} -``` - -And then extend the server schema type `User`, with a list of `Note`, called `notes`. - -```graphql -extend type User { - notes: [Note] -} -``` - -## Querying local state - -Accessing local data is no different from querying your GraphQL server, although you are required to include atleast one server field in the query. -The field can be from the server schema, or it can be schema agnostic, like an introspection field (i.e. `__typename`). - -Here, we use a [QueryRenderer](Modern-QueryRenderer.md) to get the current `User` via the `viewer` field, along with their id, name and the local list of notes. - -```javascript -// Example.js -import React from 'react'; -import { QueryRenderer, graphql } from 'react-relay'; - -const renderQuery = ({error, props}) => { - if (error) { - return
    {error.message}
    ; - } else if (props) { - return ( -
    - {props.viewer.notes.map(({id, title, body}) => ( -
    - {title} -
    -
    - {body} -
    - ))} -
    - ); - } - return
    Loading
    ; -} - -const Example = (props) => { - return ( - - ); -} -``` - -## Mutating local state - -All local data lives in the [Relay Store](Modern-RelayStore.md). -Updating local state can be done with any `updater` function. -The `commitLocalUpdate` function is especially ideal for this, because writes to local state are usually executed outside of a mutation. - -To build upon the previous example, let's try creating, updating and deleting a `Note` from the list of `notes` on `User`. - -### Create - -```javascript -import {commitLocalUpdate} from 'react-relay'; - -let tempID = 0; - -function createUserNote() { - commitLocalUpdate(environment, store => { - const user = store.getRoot().getLinkedRecord('viewer'); - const userNoteRecords = user.getLinkedRecords('notes') || []; - - // Create a unique ID. - const dataID = `client:Note:${tempID++}`; - - //Create a new note record. - const newNoteRecord = store.create(dataID, 'Note'); - - // Add the record to the user's list of notes. - user.setLinkedRecords([...userNoteRecords, newNoteRecord], 'notes'); - }); -} -``` - -Note that since this record will be rendered by the `ExampleQuery` in our `QueryRenderer`, the QueryRenderer will automatically retain this data so it isn't garbage collected. - -If no component is rendering the local data and you want to manually retain it, you can do so by calling `environment.retain()`: - -```javascript -import {createOperationDescriptor, getRequest} from 'relay-runtime'; - -// Create a query that references that record -const localDataQuery = graphql` - query LocalDataQuery { - viewer { - notes { - __typename - } - } - } -`; - -// Create an operation descriptor for the query -const request = getRequest(localDataQuery); -const operation = createOperationDescriptor(request, {} /* variables */); - - -// Tell Relay to retain this operation so any data referenced by it isn't garbage collected -// In this case, all the notes linked to the `viewer` will be retained -const disposable = environment.retain(operation); - - -// Whenever you don't need that data anymore and it's okay for Relay to garbage collect it, -// you can dispose of the retain -disposable.dispose(); -``` - -### Update - -```javascript -import {commitLocalUpdate} from 'react-relay'; - -function updateUserNote(dataID, body, title) { - commitLocalUpdate(environment, store => { - const note = store.get(dataID); - - note.setValue(body, 'body'); - note.setValue(title, 'title') - }); -} -``` - -### Delete - -```javascript -import {commitLocalUpdate} from 'react-relay'; - -function deleteUserNote(dataID) { - commitLocalUpdate(environment, store => { - const user = store.getRoot().getLinkedRecord('viewer'); - const userNoteRecords = user.getLinkedRecords('notes'); - - // Remove the note from the list of user notes. - const newUserNoteRecords = userNoteRecords.filter(x => x.getDataID() !== dataID); - - // Delete the note from the store. - store.delete(dataID); - - // Set the new list of notes. - user.setLinkedRecords(newUserNoteRecords, 'notes'); - }); -} -``` - -## Initial local state - -All new client-side schema fields default to `undefined` value. Often however, you will want to set the initial state before querying local data. -You can use an updater function via `commitLocalUpdate` to prime local state. - -```javascript -import {commitLocalUpdate} from 'react-relay'; - -commitLocalUpdate(environment, store => { - const user = store.getRoot().getLinkedRecord('viewer'); - - // initialize user notes to an empty array. - user.setLinkedRecords([], 'notes'); -}); -``` diff --git a/website/versioned_docs/version-v1.4.1/Modern-MigrationSetup.md b/website/versioned_docs/version-v1.4.1/Modern-MigrationSetup.md deleted file mode 100644 index 53f20998dea46..0000000000000 --- a/website/versioned_docs/version-v1.4.1/Modern-MigrationSetup.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -id: migration-setup -title: Migration Setup -original_id: migration-setup ---- -## Installation - -Follow the installation instructions from the [Installation and Setup](Introduction-InstallationAndSetup.md) guide. - -## Set up babel-plugin-relay for Relay Classic - -With some additional configuration, the `"relay"` babel plugin can also translate -Relay Classic `Relay.QL` literals. Most importantly, include a reference to your GraphQL Schema as either a json file or graphql schema file. - -```javascript -{ - "plugins": [ - ["relay", {"schema": "path/schema.graphql"}] - ] -} -``` - -## Set up babel-plugin-relay for "[compatibility mode](Modern-RelayCompat.md)" - -When incrementally converting a Relay Classic app to Relay Modern, `graphql` -literals can be translated to be usable by _both_ runtimes if configured to use -compatibility mode: - -```javascript -{ - "plugins": [ - ["relay", {"compat": true, "schema": "path/schema.graphql"}] - ] -} -``` - -## Additional Options - -The Relay Classic and Relay Compat modes produce generated content inline and may -catch and log any detected GraphQL validation errors, leaving those errors to be -thrown at runtime. - -When compiling code for production deployment, the plugin can be configured to immediately throw upon encountering a validation problem. The plugin can be further customized for different environments with the following options: - -```javascript -{ - "plugins": [ - ["relay", { - "compat": true, - "schema": "path/schema.graphql", - }] - ] -} -``` diff --git a/website/versioned_docs/version-v1.4.1/Modern-Mutations.md b/website/versioned_docs/version-v1.4.1/Modern-Mutations.md deleted file mode 100644 index b85c5d1a969e0..0000000000000 --- a/website/versioned_docs/version-v1.4.1/Modern-Mutations.md +++ /dev/null @@ -1,357 +0,0 @@ ---- -id: mutations -title: Mutations -original_id: mutations ---- -Table of Contents: - -- [`commitMutation`](#commitmutation) -- [Simple Example](#simple-example) -- [Optimistic Updates](#optmistic-updates) -- [Updater Configs](#updater-configs) -- [Using updater and optimisticUpdater](#using-updater-and-optimisticupdater) - -## `commitMutation` - -Use `commitMutation` to create and execute mutations. `commitMutation` has the following signature: - -```javascript -commitMutation( - environment: Environment, - config: { - mutation: GraphQLTaggedNode, - variables: {[name: string]: any}, - onCompleted?: ?(response: ?Object, errors: ?Array) => void, - onError?: ?(error: Error) => void, - optimisticResponse?: Object, - optimisticUpdater?: ?(store: RecordSourceSelectorProxy) => void, - updater?: ?(store: RecordSourceSelectorProxy, data: SelectorData) => void, - configs?: Array, - }, -); -``` - -### Arguments - -- `environment`: The [Relay Environment](Modern-RelayEnvironment.md). **Note:** To ensure the mutation is performed on the correct `environment`, it's recommended to use the environment available within components (from `this.props.relay.environment`), instead of referencing a global environment. -- `config`: - - `mutation`: The `graphql` tagged mutation query. - - `variables`: Object containing the variables needed for the mutation. For example, if the mutation defines an `$input` variable, this object should contain an `input` key, whose shape must match the shape of the data expected by the mutation as defined by the GraphQL schema. - - `onCompleted`: Callback function executed when the request is completed and the in-memory Relay store is updated with the `updater` function. Takes a `response` object, which is the "raw" server response, and `errors`, an array containing any errors from the server. . - - `onError`: Callback function executed if Relay encounters an error during the request. - - `optimisticResponse`: Object containing the data to optimistically update the local in-memory store, i.e. immediately, before the mutation request has completed. This object must have the same shape as the mutation's response type, as defined by the GraphQL schema. If provided, Relay will use the `optimisticResponse` data to update the fields on the relevant records in the local data store, _before_ `optimisticUpdater` is executed. If an error occurs during the mutation request, the optimistic update will be rolled back. - - `optimisticUpdater`: Function used to optimistically update the local in-memory store, i.e. immediately, before the mutation request has completed. If an error occurs during the mutation request, the optimistic update will be rolled back. - This function takes a `store`, which is a proxy of the in-memory [Relay Store](Modern-RelayStore.md). In this function, the client defines 'how to' update the local data via the `store` instance. For details on how to use the `store`, please refer to our [Relay Store API Reference](Modern-RelayStore.md). - **Please note:** - - It is usually preferable to just pass an `optimisticResponse` option instead of an `optimisticUpdater`, unless you need to perform updates on the local records that are more complicated than just updating fields (e.g. deleting records or adding items to collections). - - If you do decide to use an `optimisticUpdater`, often times it can be the same function as `updater`. - - `updater`: Function used to update the local in-memory store based on the **real** server response from the mutation. If `updater` is not provided, by default, Relay will know to automatically update the fields on the records referenced in the mutation response; however, you should pass an `updater` if you need to make more complicated updates than just updating fields (e.g. deleting records or adding items to collections). - When the server response comes back, Relay first reverts any changes introduced by `optimisticUpdater` or `optimisticResponse` and will then execute `updater`. - This function takes a `store`, which is a proxy of the in-memory [Relay Store](Modern-RelayStore.md). In this function, the client defines 'how to' update the local data based on the server response via the `store` instance. For details on how to use the `store`, please refer to our [Relay Store API Reference](Modern-RelayStore.md). - - `configs`: Array containing objects describing `optimisticUpdater`/`updater` configurations. `configs` provides a convenient way to specify the `updater` behavior without having to write an `updater` function. See our section on [Updater Configs](#updater-configs) for more details. - -## Simple Example - -Example of a simple mutation: - -```javascript -import {commitMutation, graphql} from 'react-relay'; - -const mutation = graphql` - mutation MarkReadNotificationMutation( - $input: MarkReadNotificationData! - ) { - markReadNotification(data: $input) { - notification { - seenState - } - } - } -`; - -function markNotificationAsRead(environment, source, storyID) { - const variables = { - input: { - source, - storyID, - }, - }; - - commitMutation( - environment, - { - mutation, - variables, - onCompleted: (response, errors) => { - console.log('Response received from server.') - }, - onError: err => console.error(err), - }, - ); -} -``` - -## Optimistic Updates - -To improve perceived responsiveness, you may wish to perform an "optimistic update", in which the client immediately updates to reflect the anticipated new value even before the response from the server has come back. The simplest way to do this is by providing an `optimisticResponse` and adding it to the `config` that we pass into `commitMutation`: - -```javascript -const mutation = graphql` - mutation MarkReadNotificationMutation( - $input: MarkReadNotificationData! - ) { - markReadNotification(data: $input) { - notification { - seenState - } - } - } -`; - -const optimisticResponse = { - markReadNotification: { - notification: { - seenState: SEEN, - }, - }, -}; - -commitMutation( - environment, - { - mutation, - optimisticResponse, - variables, - }, -); -``` - -Another way to enable optimistic updates is via the `optimisticUpdater`, which can be used for more complicated update scenarios. Using `optimisticUpdater` is covered in the section [below](#using-updater-and-optimisticupdater). - -## Updater Configs - -We can give Relay instructions in the form of a `configs` array on how to use the response from each mutation to update the client-side store. We do this by configuring the mutation with one or more of the following config types: - -### NODE_DELETE - -Given a deletedIDFieldName, Relay will remove the node(s) from the connection. - -#### Arguments - -- `deletedIDFieldName: string`: The field name in the response that contains the DataID of the deleted node - -#### Example - -```javascript -const mutation = graphql` - mutation DestroyShipMutation($input: DestroyShipData!) { - destroyShip(input: $input) { - destroyedShipId - faction { - ships { - id - } - } - } - } -`; - -const configs = [{ - type: 'NODE_DELETE', - deletedIDFieldName: 'destroyedShipId', -}]; -``` - -### RANGE_ADD - -Given a parent, information about the connection, and the name of the newly created edge in the response payload Relay will add the node to the store and attach it to the connection according to the range behavior(s) specified in the connectionInfo. - -#### Arguments - -- `parentID: string`: The DataID of the parent node that contains the - connection. -- `connectionInfo: Array<{key: string, filters?: Variables, rangeBehavior: - string}>`: An array of objects containing a connection key, an object - containing optional filters, and a range behavior depending on what behavior we expect (append, prepend, or ignore). - - `filters`: An object containing GraphQL calls e.g. `const filters = {'orderby': 'chronological'};`. -- `edgeName: string`: The field name in the response that represents the newly created edge - -#### Example - -```javascript -const mutation = graphql` - mutation AddShipMutation($input: AddShipData!) { - addShip(input: $input) { - faction { - ships { - id - } - } - newShipEdge - } - } -`; - -const configs = [{ - type: 'RANGE_ADD', - parentID: 'shipId', - connectionInfo: [{ - key: 'AddShip_ships', - rangeBehavior: 'append', - }], - edgeName: 'newShipEdge', -}]; -``` - -### RANGE_DELETE - -Given a parent, connectionKeys, one or more DataIDs in the response payload, and -a path between the parent and the connection, Relay will remove the node(s) -from the connection but leave the associated record(s) in the store. - -#### Arguments - -- `parentID: string`: The DataID of the parent node that contains the - connection. -- `connectionKeys: Array<{key: string, filters?: Variables}>`: An array of - objects containing a connection key and optionally filters. - - `filters`: An object containing GraphQL calls e.g. `const filters = {'orderby': 'chronological'};`. -- `pathToConnection: Array`: An array containing the field names between the parent and the connection, including the parent and the connection. -- `deletedIDFieldName: string | Array`: The field name in the response that contains the DataID of the removed node, or the path to the node removed from the connection - -#### Example - -```javascript -const mutation = graphql` - mutation RemoveTagsMutation($input: RemoveTagsData!) { - removeTags(input: $input) { - todo { - tags { - id - } - } - removedTagId - } - } -`; - -const configs = [{ - type: 'RANGE_DELETE', - parentID: 'todoId', - connectionKeys: [{ - key: RemoveTags_tags, - }], - pathToConnection: ['todo', 'tags'], - deletedIDFieldName: removedTagId -}]; -``` - -## Using updater and optimisticUpdater - -`updater` and `optmisticUpdater` are functions that you can pass to a `commitMutation` call when you need full control over how to update the local data store, either optimistically, or based on a server response. Often times, both of these can be the same function. - -When you provide these functions, this is roughly what happens during the mutation request: - -- If `optimisticResponse` is provided, Relay will use it to update the fields under the records as specified by the ids in the `optimisticResponse`. -- If `optimisticUpdater` is provided, Relay will execute it and update the store accordingly. -- After the network comes back, if any optimistic update was applied, it will be rolled back. -- Relay will then automatically update the fields under the record corresponding to the ids in the response payload. -- If an `updater` was provided, Relay will execute it and update the store accordingly. The server payload will be available to the `updater` as a root field in the store. - -Here are a quick example of adding a todo item to a Todo list using this [example schema](https://github.com/relayjs/relay-examples/blob/main/todo/data/schema.graphql#L36): - -```javascript -// AddTodoMutation.js -import {commitMutation, graphql} from 'react-relay'; -import {ConnectionHandler} from 'relay-runtime'; - -const mutation = graphql` - mutation AddTodoMutation($input: AddTodoInput!) { - addTodo(input:$input) { - todoEdge { - cursor - node { - complete - id - text - } - } - viewer { - id - totalCount - } - } - } -`; - -function sharedUpdater(store, user, newEdge) { - // Get the current user record from the store - const userProxy = store.get(user.id); - - // Get the user's Todo List using ConnectionHandler helper - const conn = ConnectionHandler.getConnection( - userProxy, - 'TodoList_todos', // This is the connection identifier, defined here: https://github.com/relayjs/relay-examples/blob/main/todo/js/components/TodoList.js#L68 - ); - - // Insert the new todo into the Todo List connection - ConnectionHandler.insertEdgeAfter(conn, newEdge); -} - -let tempID = 0; - -function commit( - environment, - text, - user -) { - return commitMutation( - environment, - { - mutation, - variables: { - input: { - text, - clientMutationId: tempID++, - }, - }, - updater: (store) => { - // Get the payload returned from the server - const payload = store.getRootField('addTodo'); - - // Get the edge of the newly created todo item - const newEdge = payload.getLinkedRecord('todoEdge'); - - // Add it to the user's todo list - sharedUpdater(store, user, newEdge); - }, - optimisticUpdater: (store) => { - // Create a Todo Item record in our store with a tempory ID - const id = 'client:newTodo:' + tempID++; - const node = store.create(id, 'Todo'); - node.setValue(text, 'text'); - node.setValue(id, 'id'); - - // Create a new edge that contains the newly created Todo Item - const newEdge = store.create( - 'client:newEdge:' + tempID++, - 'TodoEdge', - ); - newEdge.setLinkedRecord(node, 'node'); - - // Add it to the user's todo list - sharedUpdater(store, user, newEdge); - - // Given that we don't have a server response here, we also need to update the todo item count on the user - const userRecord = store.get(user.id); - userRecord.setValue( - userRecord.getValue('totalCount') + 1, - 'totalCount', - ); - }, - } - ); -} -``` - -For details on how to interact with the Relay Store, please refer to our Relay Store [docs](Modern-RelayStore.md). diff --git a/website/versioned_docs/version-v1.4.1/Modern-NetworkLayer.md b/website/versioned_docs/version-v1.4.1/Modern-NetworkLayer.md deleted file mode 100644 index 34a616d3b5f77..0000000000000 --- a/website/versioned_docs/version-v1.4.1/Modern-NetworkLayer.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -id: network-layer -title: Network Layer -original_id: network-layer ---- -In order to know how to access your GraphQL server, Relay Modern requires developers to provide an object implementing the `NetworkLayer` interface when creating an instance of a [Relay Environment](Modern-RelayEnvironment.md). The environment uses this network layer to execute queries, mutations, and (if your server supports them) subscriptions. This allows developers to use whatever transport (HTTP, WebSockets, etc) and authentication is most appropriate for their application, decoupling the environment from the particulars of each application's network configuration. - -Currently the easiest way to create a network layer is via a helper from the `relay-runtime` package: - -```javascript -const {Environment, Network} = require('relay-runtime'); - -// Define a function that fetches the results of an operation (query/mutation/etc) -// and returns its results as a Promise: -function fetchQuery( - operation, - variables, - cacheConfig, - uploadables, -) { - return fetch('/graphql', { - method: 'POST', - headers: { - // Add authentication and other headers here - 'content-type': 'application/json' - }, - body: JSON.stringify({ - query: operation.text, // GraphQL text from input - variables, - }), - }).then(response => { - return response.json(); - }); -} - -// Create a network layer from the fetch function -const network = Network.create(fetchQuery); - -// Create an environment using this network: -const environment = new Environment({ - ..., // other options - network, -}); -``` - -Note that this is a basic example to help you get started. This example could be extended with additional features such as request/response caching (enabled e.g. when `cacheConfig.force` is false) and uploading form data for mutations (the `uploadables` parameter). diff --git a/website/versioned_docs/version-v1.4.1/Modern-NewInRelayModern.md b/website/versioned_docs/version-v1.4.1/Modern-NewInRelayModern.md deleted file mode 100644 index ba672202bd010..0000000000000 --- a/website/versioned_docs/version-v1.4.1/Modern-NewInRelayModern.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -id: new-in-relay-modern -title: New in Relay Modern -original_id: new-in-relay-modern ---- -
    -A summary of the improvements and new features in Relay Modern. -
    - -## Modern API - -Compared to Relay Classic, the Relay Modern API has the following differentiating features: - -- A simpler, more predictable mutation API. The restrictions on mutation queries from Relay Classic are also removed: mutation queries are static, fields can be arbitrarily nested, and may use arbitrary arguments. -- When using [`QueryRenderer`](Modern-QueryRenderer.md), the restrictions on queries from Relay Classic are removed: queries may contain multiple root fields that use arbitrary arguments and return singular or plural values. The `viewer` root field is now optional. -- Routes are now optional: `QueryRenderer` can be used without defining a route. More in the [routing guide](Modern-Routing.md). -- `QueryRenderer` supports rendering small amounts of data directly, instead of requiring a container to access data. [Containers](Modern-FragmentContainer.md) are optional and can be used as your application grows in size and complexity. -- The API is overall simpler and more predictable. - -You can use [Compat mode](Modern-RelayCompat.md) to incrementally adopt Relay Modern APIs in an existing Relay app. - -## Modern Runtime - -For new Relay apps or existing apps that have been fully converted to the Modern/Compat API, the Relay Modern runtime can be enabled to activate even more features. In addition to those described above, this includes: - -### Performance - -The new Relay Modern core is more light-weight and significantly faster than the previous version. It is redesigned to work with static queries, which allow us to push more work to build/compilation time. The Modern core is much smaller as a result of removing a lot of the complex features required for dynamic queries. The new core is also an order of magnitude faster in processing the response with an optimized parsing instruction set that is generated at build time. We no longer keep around tracking information needed for dynamic query generation, which drastically reduces the memory overhead of using Relay. This means more memory is left for making the UI feel responsive. Relay Modern also supports persisted queries, reducing the upload size of the request from the full query text to a simple id. - -### Smaller Bundle Size - -The Relay runtime bundle is roughly 20% of the size of Relay Classic. - -### Garbage Collection - -The runtime automatically removes cached data that is no longer referenced, helping to reduce memory usage. - -### GraphQL Subscriptions & Live Queries - -Relay Modern supports GraphQL Subscriptions, using the imperative update API to allow modifications to the store whenever a payload is received. It also features experimental support for GraphQL Live Queries via polling. - -### Injectable Custom Field Handlers - -Some fields - especially those for paginated data - can require post-processing on the client in order to merge previously fetched data with new information. Relay Modern supports custom field handlers that can be used to process these fields to work with various pagination patterns and other use cases. - -### Simpler Mutation API - -An area we've gotten a lot of questions on was mutations and their configs. Relay Modern introduces a new mutation API that allows records and fields to be updated in a more direct manner. - -### Client Schema Extensions (Experimental) - -The Relay Modern Core adds support for client schema extensions. These allow Relay to conveniently store some extra information with data fetched from the server and be rendered like any other field fetched from the server. This should be able to replace some use cases that previously required a Flux/Redux store on the side. - -### Flow Type Generation - -Relay Modern comes with automatic Flow type generation for the fragments used in Relay containers based on the GraphQL schema. Using these Flow types can help make an application less error-prone, by ensuring all possible `null` or `undefined` cases are considered even if they don't happen frequently. - -## Fewer Requirements around Routing - -Routes no longer need to know anything about the query root in Relay Modern. Relay components can be rendered anywhere wrapped in a `QueryRenderer`. This should bring more flexibility around picking routing frameworks. - -## Extensible Core - -Relay Modern's core is essentially an un-opinionated store for GraphQL data. It can be used independent of rendering views using React and can be extended to be used with other frameworks. diff --git a/website/versioned_docs/version-v1.4.1/Modern-PaginationContainer.md b/website/versioned_docs/version-v1.4.1/Modern-PaginationContainer.md deleted file mode 100644 index a28b9ae18dc91..0000000000000 --- a/website/versioned_docs/version-v1.4.1/Modern-PaginationContainer.md +++ /dev/null @@ -1,300 +0,0 @@ ---- -id: pagination-container -title: Pagination Container -original_id: pagination-container ---- -Pagination Container is also a [higher-order component](https://reactjs.org/docs/higher-order-components.html), similar to a [Fragment Container](Modern-FragmentContainer.md), that is designed to simplify the workflow of loading more items in a list — in many cases, we don't want to fetch all the data at once but lazily load more data. It relies on a GraphQL server exposing connections in a standardized way. For a detailed spec, please check out [this page](https://facebook.github.io/relay/graphql/connections.htm). - -Table of Contents: - -- [`@connection`](#connection) -- [`createPaginationContainer`](#createpaginationcontainer) -- [`hasMore`](#hasmore) -- [`isLoading`](#isloading) -- [`loadMore`](#loadmore) -- [`refetchConnection`](#refetchconnection) -- [Pagination Example](#pagination-example) - -## `@connection` - -Pagination Container works in a very similar way to the [Fragment Container](./#fragment-container) in that you also specify the data requirements for a component via GraphQL fragments in the `fragmentSpec`. - -However, when [specifying connection fragments](#createpaginationcontainer) for a Pagination Container, it is expected that at least one of the fragments contains a [GraphQL connection](https://facebook.github.io/relay/graphql/connections.htm) to paginate over, and that that the connection field is annotated with a `@connection` directive. - -The purpose of the `@connection` directive is to allow Relay to uniquely identify different connections under a parent type. The `@connection` directive takes 2 arguments that help identify the connection: - -```graphql -@connection(key: String!, filters: [String]) - -``` - -- `key`: **Required** String that serves as a unique identifier for the connection under the parent field type. A good practice could be `_`. -- `filters`: **Optional** Array of strings that belong to the set of argument variables defined for the connection field (e.g. `orderBy`, `searchTerm`, etc). The values for the variables specified in this array will be used alongside the user-supplied `key` to uniquely identify a connection. If `filters` is not provided, by default Relay will use the set of all of the arguments the connection field takes, excluding pagination specific arguments (i.e. `first`/`last`, `after`/`before`). - -### Examples - -Specifying just the `key`: - -```javascript -fragment Feed_user on User { - # This connection, under a specific User, will be uniquely identified by - # the key "Feed_feed" and the value of `$orderBy` (given that no `filters` were provided) - feed( - first: $count - after: $cursor - orderby: $orderBy - ) @connection(key: "Feed_feed") { - edges { - node { - id, - ...Story_story - } - } -} -``` - -Specifying `key` and `filters`: - -```javascript -fragment Feed_user on User { - # This connection, under a specific User, will be uniquely identified by - # the key "Feed_feed" and /only/ the value of `$searchTerm`, i.e. - # different values of `orderBy` will not distinguish connections. - feed( - first: $count - after: $cursor - orderby: $orderBy - search_term: $searchTerm - ) @connection(key: "Feed_feed", filters: ['searchTerm']) { - edges { - node { - id, - ...Story_story - } - } -} -``` - -## `createPaginationContainer` - -`createPaginationContainer` has the following signature: - -```javascript -createPaginationContainer( - component: ReactComponentClass, - fragmentSpec: GraphQLTaggedNode | {[string]: GraphQLTaggedNode}, - connectionConfig: ConnectionConfig, -): ReactComponentClass; - -type ConnectionConfig = { - direction?: 'backward' | 'forward', - getConnectionFromProps?: (props: Object) => ?ConnectionData, - getFragmentVariables?: (previousVariables: Object, totalCount: number) => Object, - getVariables: ( - props: Object, - paginationInfo: {count: number, cursor: ?string}, - fragmentVariables: Object, - ) => Object, - query: GraphQLTaggedNode, -}; - -type ConnectionData = { - edges?: ?Array, - pageInfo?: ?{ - endCursor: ?string, - hasNextPage: boolean, - hasPreviousPage: boolean, - startCursor: ?string, - }, -}; -``` - -### Arguments - -- `component`: The React Component _class_ of the component requiring the fragment data. -- `fragmentSpec`: Specifies the data requirements for the Component via a GraphQL fragment. It is expected that one of the fragments specified here will contain a [`@connection`](#connection) for pagination. The required data will be available on the component as props that match the shape of the provided fragment. `fragmentSpec` can be one of 2 things: - - A `graphql` tagged fragment. If the fragment uses the name convention `<...>_`, the fragment's data will be available to the Component as a prop with the given ``. - If the fragment name doesn't specify a prop name, the data will be available as a `data` prop. - - An object whose keys are prop names and values are `graphql` tagged fragments. Each key specified in this object will correspond to a prop available to the resulting Component. - - **Note:** To enable [compatibility mode](Modern-RelayCompat.md), `relay-compiler` enforces fragments to be named as `_`. -- `connectionConfig`: - - `direction`: Either "forward" to indicate forward pagination using after/first, or "backward" to indicate backwards pagination using before/last. If not provided, Relay will infer the direction based on the provided `@connection` directive. **Note:** `direction` is not optional in [compatibility mode](Modern-RelayCompat.md). - - `getConnectionFromProps`: Function that should indicate which connection to paginate over, given the fragment props (i.e. the props corresponding to the `fragmentSpec`). This is necessary in most cases because the Relay can't automatically tell which connection you mean to paginate over (a container might fetch multiple fragments and connections, but can only paginate one of them). If not provided, Relay will try infer the correct connection to paginate over based on the provided `@connection` directive. See our [example](#pagination-example) for more details. **Note:** `getConnectionFromProps` is not optional in [compatibility mode](Modern-RelayCompat.md). - - `getFragmentVariables`: Function that should return the bag of variables to use for reading out the data from the store when re-rendering the component. This function takes the previous set of variables passed to the pagination `query`, and the number of elements that have been fetched in total so far. Specifically, this indicates which variables to use when reading out the data from the - local data store _after_ the new pagination `query` has been fetched. If not specified, Relay will default to using all of the previous variables and using the total count for the `count` variable. This option is analogous to [`renderVariables`](Modern-RefetchContainer.md#refetch) in the Refetch Container. See our [example](#pagination-example) for more details. - - `getVariables`: Function that should return the variables to pass to the pagination `query` when fetching it from the server, given the current `props`, `count` and `cursor`. You may set whatever variables here, as well as modify the defaults to use for after/first/before/last arguments. See our [example](#pagination-example) for more details. - - `query`: A `graphql` tagged query to be used as the pagination query to fetch more data upon calling [`loadMore`](#loadmore). - -### Available Props - -The Component resulting from `createPaginationContainer` will receive the following `props`: - -```javascript -type Props = { - relay: { - environment: Environment, - hasMore(), // See #hasMore section - isLoading(), // See #isLoading section - loadMore(), // See #loadMore section - refetchConnection(), // See #refetchConnection section - }, - // Additional props as specified by the fragmentSpec -} -``` - -- `relay`: - - `environment`: The current [Relay Environment](Modern-RelayEnvironment.md) - - `hasMore`: See `hasMore` [docs](#hasmore) - - `isLoading`: See `isLoading` [docs](#isloading) - - `loadMore`: See `loadMore` [docs](#loadmore) - - `refetchConnection `: See `refetchConnection` [docs](#refetchconnection) - -## `hasMore()` - -`hasMore` is a function available on the `relay` [prop](#available-props). This function indicates wether there are more pages to fetch from the server or not. - -```javascript -hasMore: () => boolean, - -``` - -## `isLoading` - -`isLoading` is a function available on the `relay` [prop](#available-props). This function indicates if a previous call to [`loadMore()`](#loadmore) is still pending. This is convenient for avoiding duplicate load calls. - -```javascript -isLoading: () => boolean, - -``` - -## `loadMore` - -`loadMore` is a function available on the `relay` [prop](#available-props). You can call `loadMore()` to fetch more items from the server based on the `connectionConfig` provided to the container. This will return null if there are no more items to fetch, otherwise it will fetch more items and return a Disposable that can be used to cancel the fetch. - -```javascript -loadMore(pageSize: number, callback: ?(error: ?Error) => void): ?Disposable - -``` - -### Arguments: - -- `pageSize`: The number of **additional** items to fetch (not the total). -- `callback`: Function called when the new page has been fetched. If an error occurred during refetch, this function will receive that error as an argument. - -## `refetchConnection` - -`refetchConnection` is a function available on the `relay` [prop](#available-props). You can call `refetchConnection` to restart pagination on a connection from scratch, with optionally a completely new set of variables to pass to the pagination `query`. This is useful for example if you are paginating over a collection based on a userID and the userID changes, you'd want to start paginating over the new collection for the new user. - -```javascript -refetchConnection:( - count: number, - callback: (error: ?Error) => void, - refetchVariables: ?Variables, -) => ?Disposable, - -``` - -### Arguments: - -- `totalCount`: The total number of elements to fetch -- `callback`: Function called when the new page has been fetched. If an error occurred during refetch, this function will receive that error as an argument. -- `refetchVariables`: A potentially new bag of variables to pass to the pagination `query` when fetching it from the server. - -## Pagination Example - -```javascript -// Feed.js -import {createPaginationContainer, graphql} from 'react-relay'; - -class Feed extends React.Component { - render() { - return ( -
    - {this.props.user.feed.edges.map( - edge => - )} -
    - ); - } - - _loadMore() { - if (!this.props.relay.hasMore() || this.props.relay.isLoading()) { - return; - } - - this.props.relay.loadMore( - 10, // Fetch the next 10 feed items - error => { - console.log(error); - }, - ); - } -} - -module.exports = createPaginationContainer( - Feed, - { - user: graphql` - fragment Feed_user on User - @argumentDefinitions( - count: {type: "Int", defaultValue: 10} - cursor: {type: "ID"} - orderby: {type: "[FriendsOrdering]", defaultValue: [DATE_ADDED]} - ) { - feed( - first: $count - after: $cursor - orderby: $orderBy # Non-pagination variables - ) @connection(key: "Feed_feed") { - edges { - node { - id - ...Story_story - } - } - } - } - `, - }, - { - direction: 'forward', - getConnectionFromProps(props) { - return props.user && props.user.feed; - }, - // This is also the default implementation of `getFragmentVariables` if it isn't provided. - getFragmentVariables(prevVars, totalCount) { - return { - ...prevVars, - count: totalCount, - }; - }, - getVariables(props, {count, cursor}, fragmentVariables) { - return { - count, - cursor, - orderBy: fragmentVariables.orderBy, - // userID isn't specified as an @argument for the fragment, but it should be a variable available for the fragment under the query root. - userID: fragmentVariables.userID, - }; - }, - query: graphql` - # Pagination query to be fetched upon calling `loadMore`. - # Notice that we re-use our fragment, and the shape of this query matches our fragment spec. - query FeedPaginationQuery( - $count: Int! - $cursor: ID - $orderBy: [FriendsOrdering]! - $userID: ID! - ) { - user: node(id: $userID) { - ...Feed_user @arguments(count: $count, cursor: $cursor, orderBy: $orderBy) - } - } - ` - } -); -``` diff --git a/website/versioned_docs/version-v1.4.1/Modern-PersistedQueries.md b/website/versioned_docs/version-v1.4.1/Modern-PersistedQueries.md deleted file mode 100644 index aadc6710f63bb..0000000000000 --- a/website/versioned_docs/version-v1.4.1/Modern-PersistedQueries.md +++ /dev/null @@ -1,157 +0,0 @@ ---- -id: persisted-queries -title: Persisted Queries ---- -The relay compiler supports persisted queries which is useful because: - -- the client operation text becomes just an md5 hash which is usually shorter than the real - query string. This saves upload bytes from the client to the server. - -- the server can now whitelist queries which improves security by restricting the operations - that can be executed by a client. - -## Usage on the client - -### The `--persist-output` flag - -In your `npm` script in `package.json`, run the relay compiler using the `--persist-output` flag: - -```javascript -"scripts": { - "relay": "relay-compiler --src ./src --schema ./schema.graphql --persist-output ./path/to/persisted-queries.json" -} -``` - -The `--persist-ouput` flag does 2 things: - -1. It converts all query and mutation operation texts to md5 hashes. - - For example without `--persist-output`, a generated `ConcreteRequest` might look like below: - - ```javascript - const node/*: ConcreteRequest*/ = (function(){ - //... excluded for brevity - return { - "kind": "Request", - "operationKind": "query", - "name": "TodoItemRefetchQuery", - "id": null, // NOTE: id is null - "text": "query TodoItemRefetchQuery(\n $itemID: ID!\n) {\n node(id: $itemID) {\n ...TodoItem_item_2FOrhs\n }\n}\n\nfragment TodoItem_item_2FOrhs on Todo {\n text\n isComplete\n}\n", - //... excluded for brevity - }; - })(); - - ``` - - With `--persist-output ` this becomes: - - ```javascript - const node/*: ConcreteRequest*/ = (function(){ - //... excluded for brevity - return { - "kind": "Request", - "operationKind": "query", - "name": "TodoItemRefetchQuery", - "id": "3be4abb81fa595e25eb725b2c6a87508", // NOTE: id is now an md5 hash of the query text - "text": null, // NOTE: text is null now - //... excluded for brevity - }; - })(); - - ``` - -2. It generates a JSON file at the `` you specify containing a mapping from query ids - to the corresponding operation texts. - -```javascript -"scripts": { - "relay": "relay-compiler --src ./src --schema ./schema.graphql --persist-output ./src/queryMaps/queryMap.json" -} -``` - -The example above writes the complete query map file to `./src/queryMaps/queryMap.json`. You need to ensure all the directories -leading to the `queryMap.json` file exist. - -### Network layer changes - -You'll need to modify your network layer fetch implementation to pass a documentId parameter in the POST body instead of a query parameter: - -```javascript -function fetchQuery(operation, variables,) { - return fetch('/graphql', { - method: 'POST', - headers: { - 'content-type': 'application/json' - }, - body: JSON.stringify({ - documentId: operation.id, // NOTE: pass md5 hash to the server - // query: operation.text, // this is now obsolete because text is null - variables, - }), - }).then(response => { - return response.json(); - }); -} -``` - -## Executing Persisted Queries on the Server - -To execute client requests that send persisted queries instead of query text, your server will need to be able -to lookup the query text corresponding to each id. Typically this will involve saving the output of the `--persist-output ` JSON file to a database or some other storage mechanism, and retrieving the corresponding text for the ID specified by a client. - -For universal applications where the client and server code are in one project, this is not an issue since you can place -the query map file in a common location accessible to both the client and the server. - -### Compile time push - -For applications where the client and server projects are separate, one option is to have an additional npm run script -to push the query map at compile time to a location accessible by your server: - -```javascript -"scripts": { - "push-queries": "node ./pushQueries.js", - "relay": "relay-compiler --src ./src --schema ./schema.graphql --persist-ouput && npm run push-queries" -} -``` - -Some possibilities of what you can do in `./pushQueries.js`: - -- `git push` to your server repo - -- save the query maps to a database - -### Run time push - -A second more complex option is to push your query maps to the server at runtime, without the server knowing the query ids at the start. -The client optimistically sends a query id to the server, which does not have the query map. The server then in turn requests -for the full query text from the client so it can cache the query map for subsequent requests. This is a more complex approach -requiring the client and server to interact to exchange the query maps. - -### Simple server example - -Once your server has access to the query map, you can perform the mapping. The solution varies depending on the server and -database technologies you use, so we'll just cover the most common and basic example here. - -If you use `express-graphql` and have access to the query map file, you can import the `--persist-output` JSON file directly and -perform the matching using the `matchQueryMiddleware` from [relay-compiler-plus](https://github.com/yusinto/relay-compiler-plus). - -```javascript -import Express from 'express'; -import expressGraphql from 'express-graphql'; -import {matchQueryMiddleware} from 'relay-compiler-plus'; -import queryMapJson from './path/to/persisted-queries.json'; - -const app = Express(); - -app.use('/graphql', - matchQueryMiddleware(queryMapJson), - expressGraphql({schema})); -``` - -## Using `--persist-output` and `--watch` - -It is possible to continuously generate the query map files by using the `--persist-output` and `--watch` options simultaneously. -This only makes sense for universal applications i.e. if your client and server code are in a single project -and you run them both together on localhost during development. Furthermore, in order for the server to pick up changes -to the `queryMap.json`, you'll need to have server side hot-reloading set up. The details on how to set this up -is out of the scope of this document. diff --git a/website/versioned_docs/version-v1.4.1/Modern-QueryRenderer.md b/website/versioned_docs/version-v1.4.1/Modern-QueryRenderer.md deleted file mode 100644 index 5ecb4ac41d38b..0000000000000 --- a/website/versioned_docs/version-v1.4.1/Modern-QueryRenderer.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -id: query-renderer -title: QueryRenderer -original_id: query-renderer ---- -A `QueryRenderer` is a React Component at the root of a Relay component tree. It takes a query, fetches the given query, and uses the `render` prop to render the resulting data. - -As React components, `QueryRenderer`s can be rendered anywhere that a React component can be rendered, i.e. not just at the top level but _within_ other components or containers; for example, to lazily fetch additional data for a popover. - -However, a `QueryRenderer` will not start loading its data until it is mounted, so nested `QueryRenderer` components can lead to request waterfalls if used unnecessarily. - -## Props - -- `environment`: The [Relay Environment](Modern-RelayEnvironment.md) -- `query`: The `graphql` tagged query. **Note:** To enable [compatibility mode](Modern-RelayCompat.md), `relay-compiler` enforces the query to be named as `Query`. -- `variables`: Object containing set of variables to pass to the GraphQL query, i.e. a mapping from variable name to value. **Note:** If a new set of variables if passed, the `QueryRenderer` will re-fetch the query. -- `render`: Function of type `({error, props}) => React.Node`. The output of this function will be rendered by the `QueryRenderer`. - - `props`: Object containing data obtained from the query; the shape of this object will match the shape of the query. If this object is not defined, it means that the data is still being fetched. - - `error`: Error will be defined if an error has occurred while fetching the query. - -## Example - -```javascript -// Example.js -import React from 'react'; -import { QueryRenderer, graphql } from 'react-relay'; - -class Example extends React.Component { - render() { - return ( - { - if (error) { - return
    {error.message}
    ; - } else if (props) { - return
    {props.page.name} is great!
    ; - } - return
    Loading
    ; - }} - /> - ); - } -} -``` diff --git a/website/versioned_docs/version-v1.4.1/Modern-RefetchContainer.md b/website/versioned_docs/version-v1.4.1/Modern-RefetchContainer.md deleted file mode 100644 index 5f2a2df50d693..0000000000000 --- a/website/versioned_docs/version-v1.4.1/Modern-RefetchContainer.md +++ /dev/null @@ -1,207 +0,0 @@ ---- -id: refetch-container -title: Refetch Container -original_id: refetch-container ---- -A Refetch Container is also a [higher-order component](https://reactjs.org/docs/higher-order-components.html) that works like a regular [Fragment Container](Modern-FragmentContainer.md), but provides the additional ability to fetch a new GraphQL query with different variables and re-render the component the new result. - -Table of Contents: - -- [`createRefetchContainer`](#createrefetchcontainer) -- [`refetch`](#refetch) -- [Examples](#examples) - -## `createRefetchContainer` - -`createRefetchContainer` has the following signature: - -```javascript -createRefetchContainer( - component: ReactComponentClass, - fragmentSpec: GraphQLTaggedNode | {[string]: GraphQLTaggedNode}, - refetchQuery: GraphQLTaggedNode, -): ReactComponentClass; -``` - -### Arguments - -- `component`: The React Component _class_ of the component requiring the fragment data. -- `fragmentSpec`: Specifies the data requirements for the Component via a GraphQL fragment. The required data will be available on the component as props that match the shape of the provided fragment. `fragmentSpec` can be one of 2 things: - - A `graphql` tagged fragment. If the fragment uses the name convention `<...>_`, the fragment's data will be available to the Component as a prop with the given ``. - If the fragment name doesn't specify a prop name, the data will be available as a `data` prop. - - An object whose keys are prop names and values are `graphql` tagged fragments. Each key specified in this object will correspond to a prop available to the resulting Component. - - **Note:** To enable [compatibility mode](Modern-RelayCompat.md), `relay-compiler` enforces fragments to be named as `_`. -- `refetchQuery`: A `graphql` tagged query to be fetched upon calling [`props.relay.refetch`](#refetch). As with any query, upon fetching this query, its result will be normalized into the store, any relevant subscriptions associated with the changed records will be fired, and subscribed components will re-render. - -### Available Props - -The Component resulting from `createRefetchContainer` will receive the following `props`: - -```javascript -type Props = { - relay: { - environment: Environment, - refetch(), // See #refetch section - }, - // Additional props as specified by the fragmentSpec -} -``` - -- `relay`: - - `environment`: The current [Relay Environment](Modern-RelayEnvironment.md) - - `refetch`: See `refetch` [docs](#refetch) - -## `refetch` - -`refetch` is a function available on the `relay` [prop](#available-props) which can be used to execute the `refetchQuery` and potentially re-render the component with the newly fetched data. Specifically, upon fetching the `refetchQuery`, its result will be normalized into the store, and any relevant subscriptions associated with the changed records will be fired, causing relevant components to re-render. - -**Note:** `refetch` is meant to be used for changing variables in the component's fragment. Specifically, in order for _this_ component to re-render, it must be subscribed to changes in the records affected by this query. If the fragment for the component doesn't use variables, the component won't be subscribed to changes to new records that might be fetched by this query. A common example of this is using `refetch` to fetch a new node and re-render the component with the data for the new node; in this case the fragment needs to use a variable for the node's id, otherwise the component won't pick up the changes for the new node. - -`refetch` has the following signature: - -```javascript -type RefetchOptions = { - force?: boolean, -}; - -type Disposable = { - dispose(): void, -}; - -refetch( - refetchVariables: Object | (fragmentVariables: Object) => Object, - renderVariables: ?Object, - callback: ?(error: ?Error) => void, - options?: RefetchOptions, -): Disposable, - -``` - -### Arguments - -- `refetchVariables`: - - A bag of variables to pass to the `refetchQuery` when fetching it from the server. - - Or, a function that receives the previous set of variables used to query the data, and returns a new set of variables to pass to the `refetchQuery` when fetching it from the server. -- `renderVariables`: Optional bag of variables that indicate which variables to use for reading out the data from the store when re-rendering the component. Specifically, this indicates which variables to use when querying the data from the - local data store _after_ the new query has been fetched. If not specified, the `refetchVariables` will be used. This is useful when the data you need to render in your component doesn't necessarily match the data you queried the server for. For example, to implement pagination, you would fetch a page with variables like `{first: 5, after: ''}`, but you might want to render the full collection with `{first: 10}`. -- `callback`: Function to be called after the refetch has completed. If an error occurred during refetch, this function will receive that error as an argument. -- `options`: Optional object containing set of options. - - `force`: If the [Network Layer](Modern-NetworkLayer.md) has been configured with a cache, this option forces a refetch even if the data for this query and variables is already available in the cache. - -### Return Value - -Returns a `Disposable` on which you could call `dispose()` to cancel the refetch. - -## Examples - -### Refetching latest data - -In this simple example, let's assume we want to fetch the latest data for a `TodoItem` from the server: - -```javascript -// TodoItem.js -import {createRefetchContainer, graphql} from 'react-relay'; - -class TodoItem extends React.Component { - render() { - const item = this.props.item; - return ( - - - {item.text} -