apollo-chariot

1.0.1 • Public • Published

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 to null

The query to be executed

options

Object | defaults to null

GraphQL options to be passed into the query (i.e. Variables)

render

function | defaults to null

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 to null

A component to be rendered during data load. If nothing is passed in nothing will be rendered.

renderError

function | defaults to null

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

Readme

Keywords

none

Package Sidebar

Install

npm i apollo-chariot

Weekly Downloads

17

Version

1.0.1

License

none

Last publish

Collaborators

  • pedrotheexplorer