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
}
`,
};
// error: 'fragments' does not exist on type 'typeof PokemonCard'
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`
...
`
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: