finish-line v0.3.2
Finish Line
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.
Contents
Installation
Via Yarn:
yarn add finish-lineOr via NPM:
npm install --save finish-lineUsage
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>
)API
createEnvironment
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.
with no arguments
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} {/* ... */} />with a config object
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} {/* ... */} />with a function
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} {/* ... */} />createFetchQuery
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, anduploadablesand returns an object of headers. Adds'Content-Type': 'application/json'when applicable.cache- AQueryResponseCachefrom'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)RelayEnvironment
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>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
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 graphqlqueryto. It also receives all additionalpropsprovided to theRelayRendererthat are not listed here.error- A component to render in the event of an error. It receives theerrorobject and arefreshRendererfunction aspropsalong with all additionalpropsprovided to theRelayRendererthat are not listed here. When not provided it rendersnullwhen there's an error.loading- A component to render while Relay fetches data. It receives all additionalpropsprovided to theRelayRendererthat are not listed here. When not provided it rendersnullduring loading.query- A Relaygraphqlobject.render- Works the same asQueryRenderer'srenderprop, but is called with all of thepropspassed to theRelayRendereralong with whateverpropsRelay provides. If passed, theerrorandloadingprops 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}
/>withRelayEnvironment
Wraps your components to provides a single prop of relayEnvironment which contains the following:
commitMutation- Relay'scommitMutationfunction wrapped up so you don't have to pass theenvironmentin.current- The current instance of Relay'senvironment. This comes from thecreatefunction that was given toRelayEnvironmentProvider. Generally you won't need to actually use thispropbecause Finish Line wraps Relay up so you don't have to worry about it.refresh- A function that will call thecreatefunction that was given toRelayEnvironmentProviderto replace the currentenvironment. This is handy when someone signs in or out of your application. You can also pass arguments through to yourcreateEnvironmentfunction 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>