npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

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