Skip to content

lucasconstantino/react-apollo-fragments

Repository files navigation

React Apollo Fragments

True Fragment component for react-apollo

Build status

⚠️ This package is not ready for production, and some functionality depends on a pull-request to graphql-tag

Installation

npm install react-apollo-fragments

react-apollo-fragments has peer dependency on react, react-apollo, graphql, and prop-types. Make sure to have them installed as well.

Motivation

When using fragments one thing that bothers me is that we lose some of the decoupling between child and parent components. When the fragment is used by a first-level child using fragments be declared in it's parent component is not a big problem; you are already declaring the use of the child component via importing it on the parent, after all. But when the fragment is used way below down the tree, it just becomes odd to have the querying component have so much knowledge on what fragments exactly - and many times sub-fragments - are in use down the rendering tree.

There was already some discussion on fragment composition, but the proposals did not went forward.

What I needed was some way to decouple my components once more, avoid having to define too many inner-queries to keep them decouple, and use GraphQL for the task it was meant to be used: walking through a graph.

How does it work

This project exposes a Fragment component and a substitute Query component. When a Fragment component is rendered down the tree, it will automatically present it's fragment to the parent Query component, which will then update it's query to contain the provided fragment.

The Fragment component is a render prop based component which will provide it's children with the same query result as the Query component provides. You can also provide an id prop to Fragment, which will result in that fragment's data being provided as data prop on the Fragment's children.

Usage

Say you have the following type:

type User {
  id
  name
  surname
  photo
  age
}

... and you have an Avatar fragment:

# ./avatar.gql
fragment Avatar on User {
  name
  photo
}

... which is consumed in the following user listing query:

# ./user-list.gql
query List {
  users {
    id
    ...Avatar
  }
}

... then the fragment can be used in a component Avatar as follows:

import { Fragment } from 'react-apollo-fragments'

import avatarFragment from './avatar.gql'

const Avatar = ({ user: { name, photo } }) => (
  <Fragment fragment={ avatarFragment }>
    { () => (
      <div>
        <img src={ photo } alt={ name } />
        <h3>{ name }</h3>
      </div>
    ) }
  </Fragment>
)

... and the query can be executed as in:

import { Query } from 'react-apollo-fragments'

import userListQuery from './user-list.gql'
import Avatar from './Avatar'

const Page = () => (
  <div>
    <h1>List of Users</h1>
    <Query query={ userListQuery }>
      { ({ data: { users = [] } }) => (
        <ul>
          { users.map(user => (
            <Avatar key={ user.id } user={ user } />
          )) }
        </ul>
      ) }
    </Query>
  </div>
)

... and that's it! No more direct fragment usage on the querying component :)

Fragment data injection

The Fragment can also be smarter and provide you with the fragment's data. For that to happen, all you have to do is provide the component with and id, which must match the one retrieve via getDataIdFromObject.

The Avatar component could be updated to the following:

import { Fragment } from 'react-apollo-fragments'

import avatarFragment from './avatar.gql'

const Avatar = ({ user }) => (
  <Fragment fragment={ avatarFragment } id={ getDataIdFromObject(user) }>
    { ({ data: { name, photo }, loading }) => (
      <div>
        <img src={ photo } alt={ name } />
        <h3>{ name }</h3>
      </div>
    ) }
  </Fragment>
)

This package should be temporary

I believe what is accomplished by this package should be soon implemented on the React Apollo core. If someday that happens, this package will either be deprecated or hold other experimental functionality on the subject of GraphQL fragments with Apollo and React.