apollo-chariot
v1.0.1
Published
A render prop based component for Apollo GraphQL Queries
Downloads
7
Readme
apollo-chariot
Simple React wrapper component for GraphQL queries
Disclaimer: This is by no means a rant against HoC's. Just my attempt at solving a problem in a more React like way. For a good intro into using render props instead of HoC's check out the fantastic Never Write Another HoC talk by Michael Jackson.
The problem
In order to use Apollo with a React component you must use the graphql
HOC to ehnahce that component.
This means that your component recives all the Apollo props magically. That hurts reusability, because now those components have a dependency on Apollo and it's props, composition is done statically, and the components must all handle their loading / error states individually.
Potential solution
One potential solution to this problem is to create a wrapper component.
const Profile = ({ data: { loading, profile: { name, profileImg, title } } }) => loading
? <span>Loading</span>
: (
<div>
<img src={profileImg} alt={name}/>
<h3>{name}</h3>
<small>{title}</small>
</div>
)
const profileWithData = graphql(/*... your query*/)(Profile)
export default profileWithData
export {
Profile
}
That works. We have a nice Profile component exported, and a default export of profileWithData. We can now do a default import and recieve the component and it's GraphQL data, or use the raw dumb component.
The problem is that the Profile
has a dependency on a data prop. It isn't terribly extensible. Even though its a functional component it's reusability has been hindered by the fact that it was written for a very specific GraphQL usecase.
Another problem is that profileWithData
is static composition not dynamic composition. Meaning if we want to pass in a different query, or some perhaps some query variables, that becomes another layer of abstraction we need to build.
Yet another problem is this becomes very repetitive. Any time we have a component that will recieve data from Apollo it needs to be aware of it's loading and error states and handle those.
Better solution
Disclaimer: If you are unaware of render props and how and why to use them, you really ought to watch the video above.
A better solution would be creating a reusable component that can handle fetching the data, and processing its states. And that can then pass that data to its children dynamically instead of statically. That's where ApolloChariot comes in.
With ApolloChariot we can rewrite the above component as this
const Profile = ({ data: { name, profileImg, title } }) => (
<div>
<img src={profileImg} alt={name}/>
<h3>{name}</h3>
<small>{title}</small>
</div>
)
Then to use Profile
and pass it data from Apollo you would do the following
const ProfileWithData = () => (
<ApolloChariot
query={{/* ... your query*/}}
render={({data: {profile}}) => <Profile data={profile}/>}
/>
)
You can also pass in a function as children to have that render
const ProfileWithData = () => (
<ApolloChariot query={{/* ... your query*/}}>
{
({ data: { profile } }) => <Profile data={profile} />
}
</ApolloChariot>
)
You could also do this in a route (ie React Router v4)
{/* ...your content */}
<Route
path='/profile'
exact
component={ProfileWithData}
/>
{/* If you needed to fetch profile data by id it's also easy */}
<Route
path='/profile/:id'
exact
render={({ match: { params: { id } } }) => (
<ApolloChariot
query={{/* ... your query*/}}
options={{ variables: { id } }}
render={({data: {profile}}) => <Profile data={profile}/>}
/>
)}
/>
{/* ...your content */}
ApolloChariot will also handle the loading and error states for you. Giving you the ability to simply write a component that takes data.
Props
query
GraphQL Query Document
| defaults tonull
The query to be executed
options
Object
| defaults tonull
GraphQL options to be passed into the query (i.e. Variables)
render
function
| defaults tonull
A component to be rendered with the data returned from the query. If nothing is passed in then children will be rendered as a function.
renderLoading
function
| defaults tonull
A component to be rendered during data load. If nothing is passed in nothing will be rendered.
renderError
function
| defaults tonull
A component to be rendered if an error is returned. If nothing is passed in nothing will be rendered.
Child Callback Function
This function is what gets called inside of ApolloChariot (ie <ApolloChariot>{ ...callBack }</ApolloChariot>
). The function is passed the an object containing props that come from the Apollo graphql
HoC.
It will contain your data, any errors, and whether the data is loading or not. You should not need to worry about the errors or loading state but you can access them all the same.
Callback object
| property | type | description |
|-----------------------|-----------------|----------------------------------------------------------------------------------------------|
| data
| object
/null
| The data returned from the GraphQL query |
| loading
| boolean
| A boolean representing whether or not the query is loading |
| error
| object
| An object containing any error from the query |
For more information see the Apollo docs