Handy React components and functions to cut down on some of the boiler plate in Relay Modern apps. Most of the functionality is around managing the Relay Environment.
Via Yarn:
yarn add finish-line
Or via NPM:
npm install --save finish-line
Here are a few examples. Check out the API for more specifics.
import { graphql } from 'react-relay'
import {
RelayRenderer,
RelayEnvironmentProvider,
withRelayEnvironment,
createEnvironment
} from 'finish-line'
const MyComponent = ({ somethingFromQuery }) => (
<span>{somethingFromQuery}</span>
)
const Buttons = withRelayEnvironment(({ relayEnvironment }) => (
<div>
<button onClick={() => relayEnvironment.commitMutation({ mutationExample: 'config' })}>
Mutate!
</button>
<button onClick={relayEnvironment.refresh}>
Reset!
</button>
</div>
))
const query = graphql`query { somethingFromQuery }`
const App = () => (
<RelayEnvironmentProvider create={createEnvironment}>
<div>
<h2>Some examples!</h2>
<RelayRenderer query={query} container={MyComponent} />
<Buttons />
</div>
</RelayEnvironmentProvider>
)
Creates a new Relay Environment
that you can you can pass to Relay's QueryRenderer
, commitMutation
, etc. It can also be passed to Finish Line's RelayRenderer
.
It uses Finish Line's default createFetchQuery
for the Relay Network instance.
import { QueryRenderer } from 'react-relay'
import { createEnvironment } from 'finish-line'
// ...
const environment = createEnvironment()
<QueryRenderer environment={environment} {/* ... */} />
It passes the config object through to Finish Line's createFetchQuery
.
import { QueryRenderer } from 'react-relay'
import { createEnvironment } from 'finish-line'
// ...
const environment = createEnvironment({ cache, headers })
<QueryRenderer environment={environment} {/* ... */} />
It uses the given function as the fetch query for the Network.
import { QueryRenderer } from 'react-relay'
import { createEnvironment } from 'finish-line'
// ...
const fetchQuery = (operation, variables, cacheConfig, uploadables) => {
return fetch('/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: operation.text,
variables
})
}).then(response => response.json())
const environment = createEnvironment(fetchQuery)
<QueryRenderer environment={environment} {/* ... */} />
Creates a function that you can pass to Relay's Network.create
to fetch your data. Posts JSON unless uploadables are present, in which case it posts FormData
. It can be called with no arguments or with a config object with some or all of the following:
path
- A string of where to query data from. Defaults to'/graphql'
headers
- An object containing whatever headers you need to send to the server or a function that takesoperation
,variables
,cacheConfig
, anduploadables
and returns an object of headers. Adds'Content-Type': 'application/json'
when applicable.cache
- AQueryResponseCache
from'relay-runtime'
(or something with the same interface). Clears the cache whenever a mutation is sent and caches all requests that don't have errors.credentials
- How to handle cookies with the request. Either'omit'
,'same-origin'
, or'include'
.'omit'
by default.
import { QueryResponseCache, Network } from 'relay-runtime'
import { createFetchQuery } from 'finish-line'
const fetchQuery = createFetchQuery()
const network = Network.create(fetchQuery)
// or with all options
const path = 'https://example.org/graphql'
const headers = { Authorization: 'Bearer 1234567890' }
const cache = new QueryResponseCache({ size: 250, ttl: 5 * 60 * 1000 }) // 5 minute cache
const fetchQuery = createFetchQuery({ path, headers, cache})
const network = Network.create(fetchQuery)
A component that provides access to the Relay Environment
instance and the same helper functions as withRelayEnvironment
(check it out for details). withRelayEnvironment
is a higher order component while RelayEnvironment
is a regular component that takes a function for its children
prop and renders the result of passing it the Relay Environment
.
import {
createEnvironment
RelayEnvironmentProvider,
RelayEnvironment
} from 'finish-line'
// ...
<RelayEnvironmentProvider create={createEnvironment}>
<RelayEnvironment>
{relayEnvironment => (
<div>
<h2>Example!</h2>
<p>
The current environment looks like {JSON.stringify(relayEnvironment.current)}
</p>
<button onClick={() => relayEnvironment.commitMutation({ mutationExample: 'config' })}>
Mutate!
</button>
<button onClick={() => relayEnvironment.refresh({ example: 'argument' })}>
Reset!
</button>
</div>
)}
</RelayEnvironment>
</RelayEnvironmentProvider>
A component that helps manage your application's Relay Environment
. It takes a create
prop
which is a function that returns a new instance of a Relay Environment
. It provides a few pieces of helper context
that you can access through Finish Line's withRelayEnvironment
helper (check out its documentation for more). Finish Line's RelayRenderer
must be rendered inside of RelayEnvironmentProvider
or something that provides similar context
. [Here is a comparison of the RelayEnvironmentProvider
with RelayRenderer
import {
RelayEnvironmentProvider,
RelayRenderer,
createEnvironment,
withRelayEnvironment
} from 'finish-line'
import { MyComponent } from './MyComponent'
const headers = { Authorization: 'Bearer 1234567890' }
const newAppEnvironment = () => createEnvironment({ headers })
const MyComponentWithRelayEnvironment = withRelayEnvironment(MyComponent)
// ...
<RelayEnvironmentProvider create={newAppEnvironment}>
<MyComponentWithRelayEnvironment />
<RelayRenderer {/* ... */} />
</RelayEnvironmentProvider>
RelayRenderer
is Relay's QueryRenderer
wrapped up for convenience. You don't need to pass it a Relay Environment
since it pulls it from context
, therefore it should always be rendered as a child of RelayEnvironmentProvider
(it does not need to be a direct child). It accepts the following props
:
container
- A Relay Container or some other component to pass data from the graphqlquery
to. It also receives all additionalprops
provided to theRelayRenderer
that are not listed here.error
- A component to render in the event of an error. It receives theerror
object and arefreshRenderer
function asprops
along with all additionalprops
provided to theRelayRenderer
that are not listed here. When not provided it rendersnull
when there's an error.loading
- A component to render while Relay fetches data. It receives all additionalprops
provided to theRelayRenderer
that are not listed here. When not provided it rendersnull
during loading.query
- A Relaygraphql
object.render
- Works the same asQueryRenderer
'srender
prop
, but is called with all of theprops
passed to theRelayRenderer
along with whateverprops
Relay provides. If passed, theerror
andloading
props are ignored.variables
- Variables for yourquery
.
import { graphql } from 'react-relay'
import { RelayRenderer } from 'finish-line'
import { MyContainer } from './MyContainer'
const TryAgain = ({error, refreshRenderer}) => (
<div>
<h4>Something went wrong!</h4>
<span>{error.message}</span>
<button onPress={refreshRenderer}>Try Again?</button>
</div>
)
const Loading = (props) => (
<div>Loading...</div>
)
// ...
<RelayRenderer
query={graphql`query { get { some { data } } }`}
error={TryAgain}
loading={Loading}
container={MyContainer}
/>
Wraps your components to provides a single prop
of relayEnvironment
which contains the following:
commitMutation
- Relay'scommitMutation
function wrapped up so you don't have to pass theenvironment
in.current
- The current instance of Relay'senvironment
. This comes from thecreate
function that was given toRelayEnvironmentProvider
. Generally you won't need to actually use thisprop
because Finish Line wraps Relay up so you don't have to worry about it.refresh
- A function that will call thecreate
function that was given toRelayEnvironmentProvider
to replace the currentenvironment
. This is handy when someone signs in or out of your application. You can also pass arguments through to yourcreateEnvironment
function if you'd like. If called in aRelayEnvironmentProvider
, all of theRelayEnvironmentProvider
's children will update as a result.
It accepts a wrappedComponentRef
prop
that will provide a ref
of the wrapped component when rendered.
Also, all static
functions on the wrapped component are hoisted up to the wrapper for convenience.
import React, { Component } from 'react'
import { withRelayEnvironment, RelayEnvironmentProvider } from 'finish-line'
class MyComponent extends Component {
render () {
const { relayEnvironment } = this.props
return <div>
<h2>Example!</h2>
<p>
The current environment looks like {JSON.stringify(relayEnvironment.current)}
</p>
<button onClick={() => relayEnvironment.commitMutation({ mutationExample: 'config' })}>
Mutate!
</button>
<button onClick={() => relayEnvironment.refresh('an argument for my environment creating function')}>
Reset!
</button>
</div>
}
}
const MyComponentWithRelayEnvironment = withRelayEnvironment(MyComponent)
// ...
<div>
<RelayEnvironmentProvider create={newAppEnvironment}>
<RelayRenderer container={MyComponentWithRelayEnvironment} {/* ... */} />
</RelayEnvironmentProvider>
</div>