Apollo-client: How to declare fragment in functional component in Typescript?

Created on 13 Dec 2017  路  4Comments  路  Source: apollographql/apollo-client

I can easily declare a fragment in a class component by adding a static method.

export default class PokemonCard extends React.Component {

  static fragments = {
    pokemon: gql`
      fragment PokemonCardPokemon on Pokemon {
        url
        name
      }
    `
  }
  // ...
}

What is the best way to do the same in functional component in Typescript and pass the type checking?

PokemonCard.fragments = {
  pokemon: gql`
    fragment PokemonCardPokemon on Pokemon {
      url
      name
    }
  `,
};

screen shot 2017-12-13 at 11 00 24 pm

// error: 'fragments' does not exist on type 'typeof PokemonCard'

Most helpful comment

Yeah, we ended up doing pretty much what @danielrasmuson described, except we colocate the fragment with the component, and use default and non-default exports to use this pattern:

// In PokemonCard.tsx:

const PokemonCard: SFC = () => <span />;

export default PokemonCard;

export const PokemonCardFragment = gql`
  fragment PokemonCardPokemon on Pokemon {
    id
    url
    name
  }
`;

// In the caller file:

import PokemonCard, { PokemonCardFragment } from './PokemonCard'; // <-- This is the main difference in usage

const GET_POKEMON_CARD_QUERY = gql`
  query getPokemonCard($id: ID!) {
    ...PokemonCardPokemon
  }
  ${PokemonCardFragment}
`;

All 4 comments

My understanding (feedback welcome) is that doing ReactComponent.fragments is just a convention.

We put the fragment on CommentsPage.fragments.comment by convention, and use the familiar gql helper to create it.

I'm using typescript and I've decided to opt out of using fragments this way for exactly the reason you detailed above.

Instead I have a file devoted to a fragment...

file: queries/pokemon-card.fragment.tsx

export const PokemonCardFragment = gql`
    fragment PokemonCardPokemon on Pokemon {
      id
      url
      name
    }
`

Then in your query...

const getPokemonCardQuery = gql`
  query getPokemonCard($id:ID!){
     ...PokemonCardPokemon
  }
  ${PokemonCardFragment}
`

Lastly pro tip! Include id in your fragment query and anytime that fragment is fetched it will reload all components that use that fragment data.

I.E. if you have some mutation

mutation addCard($name: String!){
   addPokemonCard(name: $name){
      ... PokemonCardPokemon
   },
   ${PokemonCardFragment}
}

And as long as your mutation returns PokemonCardPokemon it will reload all components using that information.

@danielrasmuson's answer in https://github.com/apollographql/apollo-client/issues/2722#issuecomment-353887088 is great! Closing - thanks!

Yeah, we ended up doing pretty much what @danielrasmuson described, except we colocate the fragment with the component, and use default and non-default exports to use this pattern:

// In PokemonCard.tsx:

const PokemonCard: SFC = () => <span />;

export default PokemonCard;

export const PokemonCardFragment = gql`
  fragment PokemonCardPokemon on Pokemon {
    id
    url
    name
  }
`;

// In the caller file:

import PokemonCard, { PokemonCardFragment } from './PokemonCard'; // <-- This is the main difference in usage

const GET_POKEMON_CARD_QUERY = gql`
  query getPokemonCard($id: ID!) {
    ...PokemonCardPokemon
  }
  ${PokemonCardFragment}
`;

I came across the same issue, one approach is to extend the interface FunctionComponent provided by React, for example:

import { FunctionComponent } from 'react'

interface FCWithFragment<E> extends FunctionComponent<E> {
  fragment: string
}

type YourComponentProps = {}
export const YourComponent: FCWithFragment<YourComponentProps> = (props) => {
  return ... 
}

YourComponent.fragment = gql`
...
`
Was this page helpful?
0 / 5 - 0 ratings